Developers

laboratory.love API

A public, read-friendly API for AI assistants and other software: search our published phthalate and bisphenol test results, read every sample-level measurement with its provenance, check crowdfunding status, and create checkout links that a person completes themselves. No account or API key is needed.

Using laboratory.love with AI assistants

The API is designed for AI agents and custom connectors. In an assistant that can build a custom connector from an API description, ask:

Create a custom connector for laboratory.love using its API documentation at https://www.laboratory.love/developers and OpenAPI spec at https://www.laboratory.love/openapi.json.

Assistants that support remote MCP servers can add https://www.laboratory.love/mcp (Streamable HTTP, no authentication). Its tools call the same operations as the REST API.

laboratory.love isn’t an official integration of any AI assistant; this is a public API anyone can connect.

Endpoints

All paths are relative to https://www.laboratory.love/api/v1.

GET/results

Search published results by name, brand, source, category, tag, analyte, or detection.

Read · operationId searchResults

GET/results/{id}

Full record: provenance, samples, every measurement, ratings and assumptions.

Read · operationId getResult

GET/analytes

What DEHP, DBP, BPA, BPS… mean and how they feed the ratings.

Read · operationId listAnalytes

GET/crowdfund

Products being crowdfunded for testing, with live funding status.

Read · operationId listCrowdfundingProducts

GET/crowdfund/{id}

One product’s goal, amount funded, remaining, and contribution range.

Read · operationId getCrowdfundingProduct

POST/crowdfund/{id}/checkout

Creates a Stripe-hosted link to contribute toward a product.

Write (sensitive) · operationId createContributionCheckout

GET/testing/tiers

Direct testing tiers (packages), prices, and terms.

Read · operationId listTestingTiers

POST/testing/checkout

Saves a pending testing order and creates a Stripe-hosted payment link.

Write (sensitive) · operationId createTestingCheckout

GET/checkouts/{checkout_id}

Whether a checkout made through the API is awaiting payment, paid, or expired.

Read · operationId getCheckoutStatus

Examples

Has laboratory.love tested Coca-Cola Zero?

curl "https://www.laboratory.love/api/v1/results?q=Coca-Cola%20Zero"
{
  "data": [{
    "id": "pl-72",
    "product_name": "Coca-Cola Zero",
    "source_type": "plasticlist_derived",
    "commissioned_by_laboratory_love": false,
    "sample_count": 3,
    "latest_date": "2024-07-17",
    "latest_date_basis": "sample_collection",
    "detected_analytes": [
      { "abbreviation": "BPF", "samples_detected": 1, "max_detected_ng_g": 14 }
    ],
    "below_loq_analytes": ["BBP", "BPA", "BPS", "DBP", "DEHP", "…"],
    "api_url": "https://www.laboratory.love/api/v1/results/pl-72",
    "match": "exact_name"
  }],
  "pagination": { "limit": 10, "returned": 1, "total_matches": 1, "next_cursor": null }
}

Commissioned results where DEHP was detected

curl "https://www.laboratory.love/api/v1/results?source=commissioned&analyte=DEHP&detected=true"

Measurements from GET /results/cf-116 (abridged)

"measurements": [
  { "abbreviation": "DEHP", "status": "detected",
    "value_ng_g": 42.82, "limit_of_quantitation_ng_g": null },
  { "abbreviation": "BBP", "status": "below_limit_of_quantitation",
    "value_ng_g": null, "limit_of_quantitation_ng_g": null },
  { "abbreviation": "BPA", "status": "not_detected",
    "value_ng_g": null, "limit_of_quantitation_ng_g": null }
]

Provenance

Every result carries source_type and commissioned_by_laboratory_love. Please say which kind of record you are describing:

  • laboratory_love_commissioned (ids starting cf-): we bought retail samples and paid an independent lab to test them. Lab, method, and purchase and test dates are included.
  • plasticlist_derived (ids starting pl-): historical results imported from PlasticList’s published data under CC BY 4.0. We did not run this testing. The record links to the PlasticList source; dates are sample-collection dates.

Search and detail responses come from the same records as the result pages and the CSV/JSON dataset, and carry its dataset_version.

Measurement states

Each measurement has one of four statuses. They are not interchangeable, and none of them means zero.

detected

The lab quantified it. The number is in value_ng_g (ng/g, the same as ppb). qualifier "greater_than" means the value is a lower bound.

below_limit_of_quantitation

Present below what the lab can quantify, or not quantifiable. value_ng_g is null; limit_of_quantitation_ng_g is given when the source reported it.

not_detected

Reported as not detected (PlasticList reported these as 0).

not_tested

The analyte was not part of that sample’s analysis.

Use limitations

  • Results describe the specific samples tested, at the time they were tested. They don’t prove anything about every unit, lot, the whole product line, or a brand.
  • A low or not-detected value doesn’t make a product “safe”, and a detection doesn’t make it “unsafe”.
  • Ratings (Low, Medium, High) compare one serving, averaged across samples, with a tolerable daily intake for a 70 kg adult or a 14 kg child. They are exposure context, not medical advice. Assumptions are in each record and on the methodology page.
  • An empty search means no published record matched, not that a product is free of these chemicals.

Checkouts

The two POST endpoints never charge anyone and never take card details. They return a Stripe-hosted checkout_url; the person opens it, reviews the price, and pays there. Success and cancel pages are fixed to laboratory.love.

curl -X POST "https://www.laboratory.love/api/v1/crowdfund/140/checkout" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2a9e-contribution" \
  -d '{"amount": 10}'
{
  "checkout_id": "cs_live_…",
  "checkout_url": "https://checkout.stripe.com/c/pay/cs_live_…",
  "status": "awaiting_user_payment",
  "requires_user_completion": true,
  "amount": 10,
  "currency": "USD",
  "terms": { "seller": "Very Little Gravitas Indeed, LLC (d/b/a Laboratory.love)", "…": "…" }
}
  • Contributions are whole US dollars, from $5 (or less when less remains) up to the amount still needed.
  • Send an Idempotency-Key header when retrying: the same key and body return the same checkout.
  • Check GET /checkouts/{checkout_id} and report a payment only when its status is paid. Links expire after about 24 hours.
  • Each response includes the seller, refund rules, and a link to the terms.

Limits, errors, and versions

  • About 120 read requests per minute and 10 checkout creations per 10 minutes per client. Over the limit you get 429 with Retry-After.
  • Search pages hold up to 50 results; follow next_cursor for more. Queries are limited to 100 characters.
  • Errors are JSON: {"error": {"code": "…", "message": "…"}}.
  • Result responses may be cached for up to an hour and update when we publish new results. Crowdfunding status is live within about a minute.
  • CORS is open, no cookies are used, and every call stands alone. Breaking changes will get a new path (/api/v2).

Data, privacy, and terms

Read endpoints need nothing about you. A testing checkout stores the product links and notes sent with it as part of the order. Payment and contact details are entered on Stripe’s page, not sent through the API.

Published data is licensed as described on the data page. See the privacy policy and terms of service.

Support

Questions, bugs, or data corrections: use the contact page.