{"openapi":"3.0.3","info":{"title":"Laboratory.love API","version":"1.0.0","description":"Read Laboratory.love’s published laboratory measurements of phthalates, bisphenols, and phthalate substitutes in consumer products, look up what the chemical abbreviations mean, see which products are being crowdfunded for testing, and create hosted checkout links for contributions or testing orders.\n\n**For AI agents.** Each record is either `laboratory_love_commissioned` (Laboratory.love bought retail samples and paid an independent lab) or `plasticlist_derived` (historical data imported from PlasticList with attribution; Laboratory.love did not run that testing). Say which when you answer. Keep the measurement statuses `detected`, `below_limit_of_quantitation`, `not_detected`, and `not_tested` distinct; none of them means zero. Results describe the specific samples tested at that time — not every unit, lot, or the whole brand — and a low or not-detected value is not a statement that a product is safe. Ratings are exposure context, not medical advice.\n\n**Tool classification.** All GET operations are Read. `createContributionCheckout` and `createTestingCheckout` are Writes that start a purchase: treat them as sensitive, confirm the details with the person, and remember they only return a payment link — nothing is charged until the person pays on the Stripe page. Use `getCheckoutStatus` before reporting a payment as complete.\n\n**Authentication:** none. **Rate limits:** about 120 read requests per minute and 10 checkout creations per 10 minutes per client IP; 429 responses include Retry-After. Cached read responses may be up to an hour old within a dataset version.\n\nDocumentation: https://www.laboratory.love/developers. A remote MCP server with the same operations is at https://www.laboratory.love/mcp.","contact":{"name":"Laboratory.love support","url":"https://www.laboratory.love/contact"},"termsOfService":"https://www.laboratory.love/legal?tab=terms","license":{"name":"Data: CC BY 4.0 (Laboratory.love-commissioned results); PlasticList-derived records keep their own license","url":"https://creativecommons.org/licenses/by/4.0/"}},"externalDocs":{"description":"Developer and agent documentation","url":"https://www.laboratory.love/developers"},"servers":[{"url":"https://www.laboratory.love/api/v1","description":"Production"}],"security":[],"tags":[{"name":"Results","description":"Published product-test results."},{"name":"Reference","description":"Analyte definitions."},{"name":"Crowdfunding","description":"Products being funded for testing."},{"name":"Testing orders","description":"Direct testing packages."},{"name":"Checkouts","description":"Status of checkouts created through this API."}],"paths":{"/results":{"get":{"operationId":"searchResults","tags":["Results"],"summary":"Search published test results","description":"Read. Search Laboratory.love’s published product-test records. Use this before answering whether a product, brand, or category has been tested, then call getResult for measurements. Each record is either `laboratory_love_commissioned` (Laboratory.love bought retail samples and paid an independent lab) or `plasticlist_derived` (historical data imported from PlasticList with attribution; Laboratory.love did not run that testing). Say which when you answer. Do not imply that a result represents every unit of a product. An empty result means no published record matched — not that a product is free of these chemicals. Default sort: relevance when `q` is given, otherwise most recent first; ties break on id, so paging is deterministic.","parameters":[{"name":"q","in":"query","required":false,"description":"Words to match in product name, brand, category, or tags (case- and punctuation-insensitive; every word must match). Example: \"Coca-Cola Zero\".","schema":{"type":"string","maxLength":100}},{"name":"brand","in":"query","required":false,"description":"Brand words. PlasticList records have no brand field, so this matches their product name.","schema":{"type":"string","maxLength":60}},{"name":"source","in":"query","required":false,"description":"`commissioned` = Laboratory.love-commissioned only; `plasticlist_derived` = PlasticList-derived only.","schema":{"type":"string","enum":["commissioned","plasticlist_derived"]}},{"name":"category","in":"query","required":false,"description":"Browsing category slug.","schema":{"type":"string","enum":["beverages","water","fast-food-and-restaurants","prepared-meals","baby-food-and-formula","produce-and-meat","milk-and-dairy","supplements-and-protein","pantry","personal-care-and-non-food"]}},{"name":"tag","in":"query","required":false,"description":"PlasticList source tag, e.g. sodas, baby_food, fast_food.","schema":{"type":"string","maxLength":60}},{"name":"analyte","in":"query","required":false,"description":"Abbreviation or full name from listAnalytes (e.g. DEHP, BPA). Keeps only results where that analyte was measured.","schema":{"type":"string","maxLength":60}},{"name":"detected","in":"query","required":false,"description":"With `analyte`: true = detected in at least one sample; false = measured but never detected. Without `analyte`: applies to any analyte.","schema":{"type":"boolean"}},{"name":"limit","in":"query","required":false,"description":"Results per page, 1–50.","schema":{"type":"integer","minimum":1,"maximum":50,"default":10}},{"name":"cursor","in":"query","required":false,"description":"Opaque `pagination.next_cursor` from the previous page. Only valid with the same filters and sort; repeat the request without it if you get `invalid_cursor`.","schema":{"type":"string","maxLength":200}},{"name":"sort","in":"query","required":false,"description":"`relevance` (requires q), `tested_on_desc`, `tested_on_asc`, or `name_asc`.","schema":{"type":"string","enum":["relevance","tested_on_desc","tested_on_asc","name_asc"]}}],"responses":{"200":{"description":"Matching results.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultSearchResponse"},"example":{"data":[{"id":"pl-72","product_name":"Coca-Cola Zero","brand":null,"result_url":"https://www.laboratory.love/results/pl-72","api_url":"https://www.laboratory.love/api/v1/results/pl-72","source_type":"plasticlist_derived","source_label":"PlasticList derived","commissioned_by_laboratory_love":false,"source_name":"PlasticList","sample_count":3,"latest_date":"2024-07-17","latest_date_basis":"sample_collection","category":"beverages","tags":["sodas","beverages"],"ratings":{"adult_70kg":{"phthalates":"Low","bisphenols":"High","substitutes":"Low"},"child_14kg":{"phthalates":"Medium","bisphenols":"High","substitutes":"Low"}},"detected_analytes":[{"analyte":"Bisphenol F","abbreviation":"BPF","samples_detected":1,"max_detected_ng_g":14}],"below_loq_analytes":["BBP","BPA","BPS","DBP","DEHP"],"not_detected_analytes":[],"dataset_version":"2026-09-22","match":"exact_name"}],"pagination":{"limit":10,"returned":1,"total_matches":1,"next_cursor":null},"dataset_version":"2026-09-22","caveat":"Results describe the specific samples tested at that time — not every unit, lot, or the whole brand — and a low or not-detected value is not a statement that a product is safe."}}}},"400":{"description":"Invalid input. `error.code` says which rule failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_limit","message":"limit must be an integer from 1 to 50."}}}}},"429":{"description":"Rate limited. Wait `Retry-After` seconds.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after 30 seconds."}}}}},"503":{"description":"Data source temporarily unavailable. Retry shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"results_unavailable","message":"Published results are temporarily unavailable. Retry shortly."}}}}}}}},"/results/{id}":{"get":{"operationId":"getResult","tags":["Results"],"summary":"Get one full test result","description":"Read. Returns the complete public record for one result: product identity, provenance, each sample with lot and analysis dates/methods, every analyte measurement with its status and LOQ when known, per-analyte counts, and the per-serving exposure ratings shown on the result page with all assumptions. When answering: (1) say whether the testing was commissioned by Laboratory.love or derived from PlasticList; (2) keep sample-level context (how many samples, how many had a detection); (3) distinguish detected values, below-LOQ, and not-detected — never report below-LOQ or not-detected as 0; (4) do not call a product “safe” because a value is low or not detected; (5) do not extrapolate from these samples to the whole product line, brand, or category.","parameters":[{"name":"id","in":"path","required":true,"description":"Result id from searchResults, e.g. cf-116 or pl-72.","schema":{"type":"string","pattern":"^(cf-[1-9][0-9]{0,9}|pl-[0-9]{1,10})$"}}],"responses":{"200":{"description":"The record.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultResponse"},"example":{"data":{"id":"cf-116","product":{"name":"Nana Joes Granola - Paleo Sunrise Series: Orange, Almond Butter & Pecan","brand":"Nana Joes","laboratory_love_product_id":116,"plasticlist_product_id":null,"image_url":"https://cdn.laboratory.love/PaleoSunriseOrange_NanaJoes.png","retailer_url":"https://amzn.to/4nBsoWX"},"result_url":"https://www.laboratory.love/results/cf-116","api_url":"https://www.laboratory.love/api/v1/results/cf-116","provenance":{"source_type":"laboratory_love_commissioned","source_label":"Laboratory.love commissioned","commissioned_by_laboratory_love":true,"source_name":"Laboratory.love","source_url":"https://www.laboratory.love/results/cf-116","license_url":"https://creativecommons.org/licenses/by/4.0/","attribution":"credit \"Laboratory.love\", link to the relevant Laboratory.love result or dataset page, link to the CC BY 4.0 license, and indicate whether changes were made.","laboratory":"Light Labs","description":"Laboratory.love bought these retail samples and commissioned an independent laboratory to test them."},"dates":{"latest_date":"2025-08-12","latest_date_basis":"laboratory_test"},"category":{"slug":"pantry","name":"Pantry","url":"https://www.laboratory.love/results/category/pantry"},"tags":[],"collection_notes":null,"sample_count":3,"samples":[{"sample_id":"14868,14871","lot":"9125184","analyses":[{"assay":"Phthalates","method":"GC-MS/MS","purchase_date":"2025-08-04","test_date":"2025-08-12"},{"assay":"BPA / BPS / BPF","method":"LC-MS/MS / LLMTDB","purchase_date":"2025-08-04","test_date":"2025-08-07"}],"measurements":[{"analyte":"Bis (2-ethylhexyl) phthalate","abbreviation":"DEHP","category":"Phthalates","status":"detected","value_ng_g":42.82,"qualifier":null,"limit_of_quantitation_ng_g":null,"unit":"ng/g"},{"analyte":"Benzylbutyl phthalate","abbreviation":"BBP","category":"Phthalates","status":"below_limit_of_quantitation","value_ng_g":null,"qualifier":null,"limit_of_quantitation_ng_g":null,"unit":"ng/g"},{"analyte":"Bisphenol A","abbreviation":"BPA","category":"Bisphenols","status":"not_detected","value_ng_g":null,"qualifier":null,"limit_of_quantitation_ng_g":null,"unit":"ng/g"}]}],"analyte_summary":[{"abbreviation":"DEHP","analyte":"Bis (2-ethylhexyl) phthalate","category":"Phthalates","samples_detected":3,"samples_below_loq":0,"samples_not_detected":0,"samples_not_tested":0,"min_detected_ng_g":21.2,"max_detected_ng_g":42.82,"max_is_lower_bound":false}],"summary_text":"Laboratory Love commissioned testing of 3 samples of Nana Joes Granola … DIBP was the highest measurement at 26.9–70.8 ng/g in all 3 samples …","exposure":{"ratings":{"adult_70kg":{"phthalates":{"level":"Low","percent_of_tdi_per_serving":0.1242},"bisphenols":{"level":"Low","percent_of_tdi_per_serving":0},"substitutes":{"level":"Low","percent_of_tdi_per_serving":0}},"child_14kg":{"phthalates":{"level":"Low","percent_of_tdi_per_serving":0.6209},"bisphenols":{"level":"Low","percent_of_tdi_per_serving":0},"substitutes":{"level":"Low","percent_of_tdi_per_serving":0}}},"serving_size_g":28,"serving_size_label":"1/4 cup","assumptions":{"basis":"One serving, using the mean concentration across the tested samples.","body_weights_kg":{"adult":70,"child":14},"tolerable_daily_intake_ng_per_kg_bw":{"phthalates_dehp_equivalents":50000,"bisphenols_sum":0.2,"substitutes_deht_equivalents":1000000},"phthalate_equivalency_factors":{"DEHP":1,"DBP":5,"BBP":0.1,"DINP":0.3},"substitute_equivalency_factors":{"DEHT":1,"DEHA":3.33,"DINCH":1,"DIDA":0},"rating_thresholds_percent_of_tdi":{"low_below":2.5,"medium_below":7.5},"below_loq_handling":"Below-LOQ and not-detected values count as zero (no LOQ is recorded for these samples)."},"methodology_url":"https://www.laboratory.love/risk-methodology","caveat":"Ratings compare one serving’s estimated exposure, averaged across the tested samples, with a tolerable daily intake (TDI) for an assumed body weight. They are exposure context, not medical advice or a safety verdict."},"dataset":{"version":"2026-09-22","license_url":"https://creativecommons.org/licenses/by/4.0/","citation":"Laboratory.love. “Plastic-Chemical Test Results,” version 2026-09-22. https://www.laboratory.love/data","catalog_url":"https://www.laboratory.love/data"},"caveat":"Results describe the specific samples tested at that time — not every unit, lot, or the whole brand — and a low or not-detected value is not a statement that a product is safe."},"dataset_version":"2026-09-22"}}}},"400":{"description":"Invalid input. `error.code` says which rule failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_limit","message":"limit must be an integer from 1 to 50."}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"result_not_found","message":"No published result has id \"pl-99999\". Search with GET /api/v1/results to find ids."}}}}},"429":{"description":"Rate limited. Wait `Retry-After` seconds.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after 30 seconds."}}}}},"503":{"description":"Data source temporarily unavailable. Retry shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"results_unavailable","message":"Published results are temporarily unavailable. Retry shortly."}}}}}}}},"/analytes":{"get":{"operationId":"listAnalytes","tags":["Reference"],"summary":"List analytes and rating inputs","description":"Read. Definitions the site uses for the chemicals it reports: abbreviation (DEHP, DBP, BPA, BPS …), full name, category, lab panel, and how each analyte feeds the exposure ratings (equivalency factors and TDIs). Use it to explain abbreviations or choose an `analyte` filter. It contains no toxicological claims beyond the rating inputs.","responses":{"200":{"description":"Analytes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalytesResponse"}}}},"429":{"description":"Rate limited. Wait `Retry-After` seconds.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after 30 seconds."}}}}}}}},"/crowdfund":{"get":{"operationId":"listCrowdfundingProducts","tags":["Crowdfunding"],"summary":"List crowdfunding products","description":"Read. Products people can fund toward laboratory testing, with live goal, amount funded, amount remaining, and allowed contribution range. Defaults to `status=accepting_contributions`; ordered by amount funded (highest first), then id.","parameters":[{"name":"q","in":"query","required":false,"description":"Words to match in product name or brand.","schema":{"type":"string","maxLength":100}},{"name":"status","in":"query","required":false,"description":"Filter by status, or `all`.","schema":{"type":"string","enum":["accepting_contributions","funded","testing_in_progress","tested","all"],"default":"accepting_contributions"}},{"name":"limit","in":"query","required":false,"description":"Results per page, 1–50.","schema":{"type":"integer","minimum":1,"maximum":50,"default":10}},{"name":"cursor","in":"query","required":false,"description":"Opaque `pagination.next_cursor` from the previous page. Only valid with the same filters and sort; repeat the request without it if you get `invalid_cursor`.","schema":{"type":"string","maxLength":200}}],"responses":{"200":{"description":"Products.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CrowdfundListResponse"},"example":{"data":[{"id":"140","product":{"name":"Organic Firm Tofu","brand":"Example Brand","category":"Food","image_url":null,"retailer_url":null},"status":"accepting_contributions","accepting_contributions":true,"currency":"USD","funding_goal":1995,"amount_funded":120,"amount_remaining":1875,"percent_funded":6,"contributor_count":4,"contribution":{"minimum":5,"maximum":1875,"increments":"whole US dollars"},"product_page_url":"https://www.laboratory.love/product/140","api_url":"https://www.laboratory.love/api/v1/crowdfund/140","result_id":null,"result_url":null,"result_api_url":null}],"pagination":{"limit":10,"returned":1,"total_matches":1,"next_cursor":null},"contribution_terms":{"seller":"Very Little Gravitas Indeed, LLC (d/b/a Laboratory.love)","refund_policy":"If the product does not reach its funding goal within 365 days of the contribution, the contribution is refunded automatically (or rolled into the shared pool if the contributor chooses). Contributions are non-refundable once testing begins.","what_it_funds":"A contribution funds laboratory testing of the product, not a purchase of the product. Results are published publicly on laboratory.love.","terms_url":"https://www.laboratory.love/legal?tab=terms"}}}}},"400":{"description":"Invalid input. `error.code` says which rule failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_limit","message":"limit must be an integer from 1 to 50."}}}}},"429":{"description":"Rate limited. Wait `Retry-After` seconds.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after 30 seconds."}}}}},"503":{"description":"Data source temporarily unavailable. Retry shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"results_unavailable","message":"Published results are temporarily unavailable. Retry shortly."}}}}}}}},"/crowdfund/{id}":{"get":{"operationId":"getCrowdfundingProduct","tags":["Crowdfunding"],"summary":"Get crowdfunding status for one product","description":"Read. Live funding status for one product. Once a product is tested, `result_id` links to its published result.","parameters":[{"name":"id","in":"path","required":true,"description":"Crowdfunding product id.","schema":{"type":"string","pattern":"^[1-9][0-9]{0,9}$"}}],"responses":{"200":{"description":"Product.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CrowdfundResponse"},"example":{"data":{"id":"140","product":{"name":"Organic Firm Tofu","brand":"Example Brand","category":"Food","image_url":null,"retailer_url":null},"status":"accepting_contributions","accepting_contributions":true,"currency":"USD","funding_goal":1995,"amount_funded":120,"amount_remaining":1875,"percent_funded":6,"contributor_count":4,"contribution":{"minimum":5,"maximum":1875,"increments":"whole US dollars"},"product_page_url":"https://www.laboratory.love/product/140","api_url":"https://www.laboratory.love/api/v1/crowdfund/140","result_id":null,"result_url":null,"result_api_url":null},"contribution_terms":{"seller":"Very Little Gravitas Indeed, LLC (d/b/a Laboratory.love)","refund_policy":"If the product does not reach its funding goal within 365 days of the contribution, the contribution is refunded automatically (or rolled into the shared pool if the contributor chooses). Contributions are non-refundable once testing begins.","what_it_funds":"A contribution funds laboratory testing of the product, not a purchase of the product. Results are published publicly on laboratory.love.","terms_url":"https://www.laboratory.love/legal?tab=terms"}}}}},"400":{"description":"Invalid input. `error.code` says which rule failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_limit","message":"limit must be an integer from 1 to 50."}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"result_not_found","message":"No published result has id \"pl-99999\". Search with GET /api/v1/results to find ids."}}}}},"429":{"description":"Rate limited. Wait `Retry-After` seconds.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after 30 seconds."}}}}},"503":{"description":"Data source temporarily unavailable. Retry shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"results_unavailable","message":"Published results are temporarily unavailable. Retry shortly."}}}}}}}},"/crowdfund/{id}/checkout":{"post":{"operationId":"createContributionCheckout","tags":["Crowdfunding"],"summary":"Create a contribution checkout link","description":"Write — starts a purchase; treat as sensitive and confirm the product and amount with the person first. Creates a secure Stripe-hosted checkout page for a contribution toward testing one product and returns its URL. This call charges nothing and takes no card details: the person must open `checkout_url` and pay. Success and cancel pages are fixed to laboratory.love. Side effects: creates a Stripe Checkout session (expires after about 24 hours if unused). Use an Idempotency-Key when retrying.","parameters":[{"name":"id","in":"path","required":true,"description":"Crowdfunding product id.","schema":{"type":"string","pattern":"^[1-9][0-9]{0,9}$"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional. Retrying with the same key and the same body returns the original checkout instead of creating another one. Reusing a key with a different body returns 409.","schema":{"type":"string","pattern":"^[A-Za-z0-9_.:-]{8,100}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContributionCheckoutRequest"},"example":{"amount":10}}}},"responses":{"201":{"description":"Checkout created; awaiting the person’s payment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContributionCheckout"},"example":{"checkout_id":"cs_live_a1B2c3D4e5F6g7H8","checkout_url":"https://checkout.stripe.com/c/pay/cs_live_a1B2c3D4e5F6g7H8","status":"awaiting_user_payment","requires_user_completion":true,"amount":10,"currency":"USD","expires_at":"2026-10-04T18:00:00.000Z","product":{"id":"140","name":"Organic Firm Tofu","brand":"Example Brand","amount_remaining_before_contribution":1875,"product_page_url":"https://www.laboratory.love/product/140"},"terms":{"seller":"Very Little Gravitas Indeed, LLC (d/b/a Laboratory.love)","refund_policy":"If the product does not reach its funding goal within 365 days of the contribution, the contribution is refunded automatically (or rolled into the shared pool if the contributor chooses). Contributions are non-refundable once testing begins.","what_it_funds":"A contribution funds laboratory testing of the product, not a purchase of the product. Results are published publicly on laboratory.love.","terms_url":"https://www.laboratory.love/legal?tab=terms"},"next_step":"Give checkout_url to the person. Nothing is charged until they open it and pay on the secure Stripe page. Check GET /api/v1/checkouts/{checkout_id} for the outcome."}}}},"400":{"description":"Invalid input. `error.code` says which rule failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_limit","message":"limit must be an integer from 1 to 50."}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"result_not_found","message":"No published result has id \"pl-99999\". Search with GET /api/v1/results to find ids."}}}}},"409":{"description":"The request conflicts with the record’s current state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"not_accepting_contributions","message":"This product is not accepting contributions (status: tested)."}}}}},"413":{"description":"Request body too large.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"body_too_large","message":"Request bodies are limited to 8192 bytes."}}}}},"415":{"description":"Body is not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"unsupported_media_type","message":"Send the request body as application/json."}}}}},"429":{"description":"Rate limited. Wait `Retry-After` seconds.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after 30 seconds."}}}}},"502":{"description":"The payment provider failed. Nothing was charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"payment_provider_error","message":"The payment provider could not create a checkout. Nothing was charged. Retry with the same Idempotency-Key."}}}}},"503":{"description":"Data source temporarily unavailable. Retry shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"results_unavailable","message":"Published results are temporarily unavailable. Retry shortly."}}}}}}}},"/testing/tiers":{"get":{"operationId":"listTestingTiers","tags":["Testing orders"],"summary":"List direct testing tiers (packages)","description":"Read. Packages a person can buy to have Laboratory.love test products they choose (three retail samples per product, 18 phthalates plus BPA, BPS, and BPF), with prices, turnaround, refund rules, and publication terms. Show the price and terms before creating a testing checkout.","responses":{"200":{"description":"Packages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestingTiersResponse"},"example":{"data":[{"id":"single","name":"1 Product — Focused Check","price":1995,"currency":"USD","products_included":1,"samples_per_product":3,"lab_tests_total":6,"panels":["18 phthalates","BPA, BPS and BPF"],"best_for":"Best for a single staple, baby food, supplement, or product you use daily."}],"terms":{"seller":"Very Little Gravitas Indeed, LLC (d/b/a Laboratory.love)","turnaround":"Reports are typically delivered 6–8 weeks after order confirmation; timing is not guaranteed.","refund_policy":"If a requested product cannot be sourced or tested, that portion of the order is refunded in full (or a replacement product is agreed). Sales are final once product sourcing begins.","publication":"Unless the buyer opts out in writing before testing begins, results may be published on laboratory.love. Personal information is never published.","terms_url":"https://www.laboratory.love/legal?tab=terms"},"order_page_url":"https://www.laboratory.love/testing/select","service_page_url":"https://www.laboratory.love/testing/phthalates-and-bisphenols"}}}},"429":{"description":"Rate limited. Wait `Retry-After` seconds.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after 30 seconds."}}}}}}}},"/testing/checkout":{"post":{"operationId":"createTestingCheckout","tags":["Testing orders"],"summary":"Create a testing order checkout link","description":"Write — starts a purchase; treat as sensitive and confirm the package, total price, and product links with the person first. Saves a pending order with the submitted product links and creates a secure Stripe-hosted checkout page for it. This call charges nothing and takes no card details: the person must open `checkout_url` and pay; an unpaid order is never processed. Use an Idempotency-Key when retrying.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional. Retrying with the same key and the same body returns the original checkout instead of creating another one. Reusing a key with a different body returns 409.","schema":{"type":"string","pattern":"^[A-Za-z0-9_.:-]{8,100}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestingCheckoutRequest"},"example":{"tier":"single","products":[{"url":"https://www.example.com/products/organic-tofu","notes":"14 oz block"}]}}}},"responses":{"201":{"description":"Checkout created; awaiting the person’s payment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestingCheckout"},"example":{"checkout_id":"cs_live_z9Y8x7W6v5U4t3S2","checkout_url":"https://checkout.stripe.com/c/pay/cs_live_z9Y8x7W6v5U4t3S2","status":"awaiting_user_payment","requires_user_completion":true,"amount":1995,"currency":"USD","expires_at":"2026-10-04T18:00:00.000Z","tier":{"id":"single","name":"1 Product — Focused Check","price":1995,"currency":"USD","products_included":1,"samples_per_product":3,"lab_tests_total":6,"panels":["18 phthalates","BPA, BPS and BPF"],"best_for":"Best for a single staple, baby food, supplement, or product you use daily."},"products_submitted":[{"url":"https://www.example.com/products/organic-tofu","notes":"14 oz block"}],"terms":{"seller":"Very Little Gravitas Indeed, LLC (d/b/a Laboratory.love)","turnaround":"Reports are typically delivered 6–8 weeks after order confirmation; timing is not guaranteed.","refund_policy":"If a requested product cannot be sourced or tested, that portion of the order is refunded in full (or a replacement product is agreed). Sales are final once product sourcing begins.","publication":"Unless the buyer opts out in writing before testing begins, results may be published on laboratory.love. Personal information is never published.","terms_url":"https://www.laboratory.love/legal?tab=terms"},"next_step":"Give checkout_url to the person. Nothing is charged until they open it and pay on the secure Stripe page. Check GET /api/v1/checkouts/{checkout_id} for the outcome."}}}},"400":{"description":"Invalid input. `error.code` says which rule failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_limit","message":"limit must be an integer from 1 to 50."}}}}},"413":{"description":"Request body too large.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"body_too_large","message":"Request bodies are limited to 8192 bytes."}}}}},"415":{"description":"Body is not JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"unsupported_media_type","message":"Send the request body as application/json."}}}}},"429":{"description":"Rate limited. Wait `Retry-After` seconds.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after 30 seconds."}}}}},"502":{"description":"The payment provider failed. Nothing was charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"payment_provider_error","message":"The payment provider could not create a checkout. Nothing was charged. Retry with the same Idempotency-Key."}}}}},"503":{"description":"Data source temporarily unavailable. Retry shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"results_unavailable","message":"Published results are temporarily unavailable. Retry shortly."}}}}}}}},"/checkouts/{checkout_id}":{"get":{"operationId":"getCheckoutStatus","tags":["Checkouts"],"summary":"Get the status of a checkout","description":"Read. Whether a checkout created through this API is awaiting payment, paid, still processing, or expired. Report a payment as complete only when status is `paid`; if it is still awaiting payment, offer the same `checkout_url` instead of creating another checkout. Returns no payer details.","parameters":[{"name":"checkout_id","in":"path","required":true,"description":"checkout_id from a checkout response.","schema":{"type":"string","pattern":"^cs_(live|test)_[A-Za-z0-9]{10,200}$"}}],"responses":{"200":{"description":"Status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutStatus"},"example":{"checkout_id":"cs_live_a1B2c3D4e5F6g7H8","kind":"crowdfund_contribution","status":"paid","amount":10,"currency":"USD","created_at":"2026-10-03T18:00:00.000Z","expires_at":"2026-10-04T18:00:00.000Z","checkout_url":null,"product_id":"140","receipt":"Stripe emails a receipt to the address entered at checkout.","support_url":"https://www.laboratory.love/contact"}}}},"400":{"description":"Invalid input. `error.code` says which rule failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_limit","message":"limit must be an integer from 1 to 50."}}}}},"404":{"description":"No such record.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"result_not_found","message":"No published result has id \"pl-99999\". Search with GET /api/v1/results to find ids."}}}}},"429":{"description":"Rate limited. Wait `Retry-After` seconds.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after 30 seconds."}}}}},"502":{"description":"The payment provider failed. Nothing was charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"payment_provider_error","message":"The payment provider could not create a checkout. Nothing was charged. Retry with the same Idempotency-Key."}}}}}}}},"/":{"get":{"operationId":"getApiIndex","tags":["Reference"],"summary":"API entry point","description":"Read. Links to this document, the documentation, the MCP endpoint, and support/legal pages.","responses":{"200":{"description":"Index.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiIndex"}}}},"429":{"description":"Rate limited. Wait `Retry-After` seconds.","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too many requests. Retry after 30 seconds."}}}}}}}}},"components":{"schemas":{"Error":{"type":"object","description":"Every non-2xx response has this shape.","additionalProperties":false,"required":["error"],"properties":{"error":{"type":"object","description":"Error detail.","additionalProperties":false,"required":["code","message"],"properties":{"code":{"type":"string","description":"Stable machine-readable error code, e.g. invalid_limit, result_not_found, rate_limited."},"message":{"type":"string","description":"Human-readable explanation, safe to show to a person."},"details":{"type":"object","description":"Extra structured context, such as allowed values.","additionalProperties":true}}}}},"Pagination":{"type":"object","description":"Deterministic pagination: the same query and cursor always return the same page for a given dataset version.","additionalProperties":false,"required":["limit","returned","total_matches","next_cursor"],"properties":{"limit":{"type":"integer","description":"Requested page size."},"returned":{"type":"integer","description":"Items in this page."},"total_matches":{"type":"integer","description":"Items matching the filters across all pages."},"next_cursor":{"type":"string","description":"Pass as `cursor` for the next page; null on the last page.","nullable":true}}},"ApiIndex":{"type":"object","description":"Entry point with links.","additionalProperties":false,"required":["name","version","description","base_url","openapi_url","documentation_url","mcp_url","authentication","support_url","privacy_url","terms_url"],"properties":{"name":{"type":"string","description":"API name."},"version":{"type":"string","description":"API version."},"description":{"type":"string","description":"What the API offers."},"base_url":{"type":"string","description":"Base URL for all endpoints.","format":"uri"},"openapi_url":{"type":"string","description":"This document.","format":"uri"},"documentation_url":{"type":"string","description":"Human and agent documentation.","format":"uri"},"mcp_url":{"type":"string","description":"Remote MCP endpoint exposing the same operations as tools.","format":"uri"},"authentication":{"type":"string","description":"Authentication requirement.","enum":["none"]},"support_url":{"type":"string","description":"Support contact page.","format":"uri"},"privacy_url":{"type":"string","description":"Privacy policy.","format":"uri"},"terms_url":{"type":"string","description":"Terms of service.","format":"uri"}}},"ResultSummary":{"type":"object","description":"One search hit. Fetch `api_url` for measurements.","additionalProperties":false,"required":["id","product_name","brand","result_url","api_url","source_type","source_label","commissioned_by_laboratory_love","source_name","sample_count","latest_date","latest_date_basis","category","tags","ratings","detected_analytes","below_loq_analytes","not_detected_analytes","dataset_version"],"properties":{"id":{"type":"string","pattern":"^(cf-[1-9][0-9]{0,9}|pl-[0-9]{1,10})$","description":"Stable result id. `cf-` = Laboratory.love commissioned, `pl-` = PlasticList derived."},"product_name":{"type":"string","description":"Product name as published."},"brand":{"type":"string","description":"Brand, when recorded. PlasticList records have none; the brand is usually part of the product name.","nullable":true},"result_url":{"type":"string","description":"Public result page.","format":"uri"},"api_url":{"type":"string","description":"Full record: GET this URL.","format":"uri"},"source_type":{"type":"string","description":"Provenance. Do not merge the two kinds when summarizing.","enum":["laboratory_love_commissioned","plasticlist_derived"]},"source_label":{"type":"string","description":"Label the site shows for the source."},"commissioned_by_laboratory_love":{"type":"boolean","description":"True only when Laboratory.love bought the samples and commissioned the lab testing."},"source_name":{"type":"string","description":"Laboratory.love or PlasticList."},"sample_count":{"type":"integer","description":"Number of distinct physical samples tested.","minimum":0},"latest_date":{"type":"string","description":"Most recent date for the record (YYYY-MM-DD).","format":"date","nullable":true},"latest_date_basis":{"type":"string","description":"What latest_date measures: the lab test date (commissioned) or the sample collection date (PlasticList).","enum":["laboratory_test","sample_collection",null],"nullable":true},"category":{"type":"string","description":"Browsing category slug.","nullable":true},"tags":{"type":"array","items":{"type":"string"},"description":"Source tags (PlasticList records)."},"ratings":{"type":"object","description":"Rating levels per serving for a 70 kg adult and a 14 kg child. Null when no serving size is recorded.","additionalProperties":false,"required":["adult_70kg","child_14kg"],"properties":{"adult_70kg":{"type":"object","description":"Exposure rating level per chemical group for one serving.","additionalProperties":false,"required":["phthalates","bisphenols","substitutes"],"properties":{"phthalates":{"type":"string","description":"Rating level.","enum":["Low","Medium","High","Not rated"]},"bisphenols":{"type":"string","description":"Rating level.","enum":["Low","Medium","High","Not rated"]},"substitutes":{"type":"string","description":"Rating level.","enum":["Low","Medium","High","Not rated"]}}},"child_14kg":{"type":"object","description":"Exposure rating level per chemical group for one serving.","additionalProperties":false,"required":["phthalates","bisphenols","substitutes"],"properties":{"phthalates":{"type":"string","description":"Rating level.","enum":["Low","Medium","High","Not rated"]},"bisphenols":{"type":"string","description":"Rating level.","enum":["Low","Medium","High","Not rated"]},"substitutes":{"type":"string","description":"Rating level.","enum":["Low","Medium","High","Not rated"]}}}},"nullable":true},"detected_analytes":{"type":"array","items":{"type":"object","description":"An analyte measured above the limit of quantitation in at least one sample.","additionalProperties":false,"required":["analyte","abbreviation","samples_detected","max_detected_ng_g"],"properties":{"analyte":{"type":"string","description":"Analyte name."},"abbreviation":{"type":"string","description":"Abbreviation, when the site uses one.","nullable":true},"samples_detected":{"type":"integer","description":"Samples with a quantified value.","minimum":1},"max_detected_ng_g":{"type":"number","description":"Highest measured value, ng/g.","nullable":true}}},"description":"Analytes detected in at least one sample."},"below_loq_analytes":{"type":"array","items":{"type":"string"},"description":"Analytes never detected and below the limit of quantitation in at least one sample (abbreviation or name)."},"not_detected_analytes":{"type":"array","items":{"type":"string"},"description":"Analytes reported as not detected in every tested sample (abbreviation or name)."},"dataset_version":{"type":"string","description":"Published dataset version this record belongs to."},"match":{"type":"string","description":"How a `q` search matched: exact_name, name_prefix, or all_words. Present only when q is given.","enum":["exact_name","name_prefix","all_words"]}}},"ResultSearchResponse":{"type":"object","description":"Search response.","additionalProperties":false,"required":["data","pagination","dataset_version","caveat"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ResultSummary"},"description":"Matching results for this page."},"pagination":{"$ref":"#/components/schemas/Pagination"},"dataset_version":{"type":"string","description":"Published dataset version searched."},"caveat":{"type":"string","description":"Interpretation caveat to carry into answers."}}},"Measurement":{"type":"object","description":"One analyte result for one sample.","additionalProperties":false,"required":["analyte","abbreviation","category","status","value_ng_g","qualifier","limit_of_quantitation_ng_g","unit"],"properties":{"analyte":{"type":"string","description":"Analyte name."},"abbreviation":{"type":"string","description":"Abbreviation (DEHP, BPA, …) when the site uses one.","nullable":true},"category":{"type":"string","description":"Phthalates, Bisphenols, Phthalate Substitutes, or Other."},"status":{"type":"string","description":"detected = quantified value in value_ng_g; below_limit_of_quantitation = present below the lab’s LOQ or not quantifiable (value is null, LOQ given when known); not_detected = reported as not detected (or as 0 by PlasticList); not_tested = not part of this sample’s analysis. Never treat these as interchangeable or as zero.","enum":["detected","below_limit_of_quantitation","not_detected","not_tested"]},"value_ng_g":{"type":"number","description":"Measured concentration in ng/g (ppb). Only set when status is detected.","nullable":true},"qualifier":{"type":"string","description":"greater_than when the lab reported the value as a lower bound (above its quantification range).","enum":["greater_than",null],"nullable":true},"limit_of_quantitation_ng_g":{"type":"number","description":"LOQ in ng/g, when the source reported one for a below-LOQ result.","nullable":true},"unit":{"type":"string","description":"Always ng/g.","enum":["ng/g"]}}},"Analysis":{"type":"object","description":"Laboratory analysis metadata (commissioned results).","additionalProperties":false,"required":["assay","method","purchase_date","test_date"],"properties":{"assay":{"type":"string","description":"Assay/panel name.","nullable":true},"method":{"type":"string","description":"Analytical method, e.g. GC-MS/MS.","nullable":true},"purchase_date":{"type":"string","description":"Date the sample was bought/ordered.","format":"date","nullable":true},"test_date":{"type":"string","description":"Date the lab tested it.","format":"date","nullable":true}}},"Sample":{"type":"object","description":"One physical sample.","additionalProperties":false,"required":["sample_id","lot","analyses","measurements"],"properties":{"sample_id":{"type":"string","description":"Sample identifier from the source (lab sample ids may be comma-joined when one physical sample had several lab rows)."},"lot":{"type":"string","description":"Lot number, when recorded.","nullable":true},"analyses":{"type":"array","items":{"$ref":"#/components/schemas/Analysis"},"description":"Assays run on this sample. Empty when the source did not publish them."},"measurements":{"type":"array","items":{"$ref":"#/components/schemas/Measurement"},"description":"Every analyte result for this sample."}}},"AnalyteSummary":{"type":"object","description":"Per-analyte counts across samples.","additionalProperties":false,"required":["abbreviation","analyte","category","samples_detected","samples_below_loq","samples_not_detected","samples_not_tested","min_detected_ng_g","max_detected_ng_g","max_is_lower_bound"],"properties":{"abbreviation":{"type":"string","description":"Abbreviation, when the site uses one.","nullable":true},"analyte":{"type":"string","description":"Analyte name."},"category":{"type":"string","description":"Chemical category."},"samples_detected":{"type":"integer","description":"Samples with a quantified value."},"samples_below_loq":{"type":"integer","description":"Samples below the limit of quantitation."},"samples_not_detected":{"type":"integer","description":"Samples reported as not detected."},"samples_not_tested":{"type":"integer","description":"Samples where the analyte was not part of the analysis."},"min_detected_ng_g":{"type":"number","description":"Lowest quantified value, ng/g.","nullable":true},"max_detected_ng_g":{"type":"number","description":"Highest quantified value, ng/g.","nullable":true},"max_is_lower_bound":{"type":"boolean","description":"True when the highest value was reported as “greater than”."}}},"Rating":{"type":"object","description":"One group’s rating.","additionalProperties":false,"required":["level","percent_of_tdi_per_serving"],"properties":{"level":{"type":"string","description":"Low (<2.5% of the TDI per serving), Medium (2.5–7.5%), High (≥7.5%), or Not rated.","enum":["Low","Medium","High","Not rated"]},"percent_of_tdi_per_serving":{"type":"number","description":"Estimated share of the tolerable daily intake from one serving, in percent. Can exceed 100.","nullable":true},"not_rated_reason":{"type":"string","description":"Why a rating is withheld, when it is."}}},"RatingSet":{"type":"object","description":"Ratings for one body weight.","additionalProperties":false,"required":["phthalates","bisphenols","substitutes"],"properties":{"phthalates":{"$ref":"#/components/schemas/Rating"},"bisphenols":{"$ref":"#/components/schemas/Rating"},"substitutes":{"$ref":"#/components/schemas/Rating"}}},"Exposure":{"type":"object","description":"Laboratory.love’s per-serving exposure context, the same numbers the result page shows. Not medical advice or a safety verdict.","additionalProperties":false,"required":["ratings","serving_size_g","serving_size_label","assumptions","methodology_url","caveat"],"properties":{"ratings":{"type":"object","description":"Ratings by assumed body weight.","additionalProperties":false,"required":["adult_70kg","child_14kg"],"properties":{"adult_70kg":{"$ref":"#/components/schemas/RatingSet"},"child_14kg":{"$ref":"#/components/schemas/RatingSet"}},"nullable":true},"serving_size_g":{"type":"number","description":"Serving size used, grams.","nullable":true},"serving_size_label":{"type":"string","description":"Serving size as labeled.","nullable":true},"assumptions":{"type":"object","description":"Inputs of the exposure calculation.","additionalProperties":false,"required":["basis","body_weights_kg","tolerable_daily_intake_ng_per_kg_bw","phthalate_equivalency_factors","substitute_equivalency_factors","rating_thresholds_percent_of_tdi","below_loq_handling"],"properties":{"basis":{"type":"string","description":"What one rating represents."},"body_weights_kg":{"type":"object","description":"Assumed body weights.","additionalProperties":false,"required":["adult","child"],"properties":{"adult":{"type":"number","description":"kg"},"child":{"type":"number","description":"kg"}}},"tolerable_daily_intake_ng_per_kg_bw":{"type":"object","description":"TDIs used.","additionalProperties":false,"required":["phthalates_dehp_equivalents","bisphenols_sum","substitutes_deht_equivalents"],"properties":{"phthalates_dehp_equivalents":{"type":"number","description":"ng/kg bw/day"},"bisphenols_sum":{"type":"number","description":"ng/kg bw/day"},"substitutes_deht_equivalents":{"type":"number","description":"ng/kg bw/day"}}},"phthalate_equivalency_factors":{"type":"object","description":"DEHP-equivalent potency factors.","additionalProperties":{"type":"number"}},"substitute_equivalency_factors":{"type":"object","description":"DEHT-equivalent potency factors.","additionalProperties":{"type":"number"}},"rating_thresholds_percent_of_tdi":{"type":"object","description":"Rating cut-offs.","additionalProperties":false,"required":["low_below","medium_below"],"properties":{"low_below":{"type":"number","description":"%"},"medium_below":{"type":"number","description":"%"}}},"below_loq_handling":{"type":"string","description":"How below-LOQ values enter the calculation for this record."}}},"methodology_url":{"type":"string","description":"Methodology page.","format":"uri"},"caveat":{"type":"string","description":"How to present ratings."}}},"ResultRecord":{"type":"object","description":"Complete public record for one result.","additionalProperties":false,"required":["id","product","result_url","api_url","provenance","dates","category","tags","collection_notes","sample_count","samples","analyte_summary","summary_text","exposure","dataset","caveat"],"properties":{"id":{"type":"string","pattern":"^(cf-[1-9][0-9]{0,9}|pl-[0-9]{1,10})$","description":"Stable result id."},"product":{"type":"object","description":"Product identity.","additionalProperties":false,"required":["name","brand","laboratory_love_product_id","plasticlist_product_id","image_url","retailer_url"],"properties":{"name":{"type":"string","description":"Product name.","nullable":true},"brand":{"type":"string","description":"Brand, when recorded.","nullable":true},"laboratory_love_product_id":{"type":"integer","description":"Laboratory.love product id (commissioned results; same id as GET /crowdfund/{id}).","nullable":true},"plasticlist_product_id":{"type":"string","description":"Original PlasticList product id (PlasticList-derived results).","nullable":true},"image_url":{"type":"string","description":"Product photo.","format":"uri","nullable":true},"retailer_url":{"type":"string","description":"Where the product is sold (may be an affiliate link).","format":"uri","nullable":true}}},"result_url":{"type":"string","description":"Public result page.","format":"uri"},"api_url":{"type":"string","description":"This record.","format":"uri"},"provenance":{"type":"object","description":"Where the data comes from. Always report this.","additionalProperties":false,"required":["source_type","source_label","commissioned_by_laboratory_love","source_name","source_url","license_url","attribution","laboratory","description"],"properties":{"source_type":{"type":"string","description":"Provenance.","enum":["laboratory_love_commissioned","plasticlist_derived"]},"source_label":{"type":"string","description":"Label the site shows."},"commissioned_by_laboratory_love":{"type":"boolean","description":"True only for Laboratory.love-commissioned testing."},"source_name":{"type":"string","description":"Laboratory.love or PlasticList."},"source_url":{"type":"string","description":"Original source page.","format":"uri","nullable":true},"license_url":{"type":"string","description":"License for reuse.","format":"uri"},"attribution":{"type":"string","description":"Required attribution when reusing.","nullable":true},"laboratory":{"type":"string","description":"Testing laboratory, when known.","nullable":true},"description":{"type":"string","description":"Plain-language provenance statement to relay."}}},"dates":{"type":"object","description":"Record-level date. Per-sample purchase and test dates are in samples[].analyses.","additionalProperties":false,"required":["latest_date","latest_date_basis"],"properties":{"latest_date":{"type":"string","description":"YYYY-MM-DD","format":"date","nullable":true},"latest_date_basis":{"type":"string","description":"laboratory_test or sample_collection.","enum":["laboratory_test","sample_collection",null],"nullable":true}}},"category":{"type":"object","description":"Browsing category.","additionalProperties":false,"required":["slug","name","url"],"properties":{"slug":{"type":"string","description":"Slug."},"name":{"type":"string","description":"Name."},"url":{"type":"string","description":"Category page.","format":"uri"}},"nullable":true},"tags":{"type":"array","items":{"type":"string"},"description":"Source tags."},"collection_notes":{"type":"string","description":"Source notes about sample collection (PlasticList), which may mention location.","nullable":true},"sample_count":{"type":"integer","description":"Distinct physical samples.","minimum":0},"samples":{"type":"array","items":{"$ref":"#/components/schemas/Sample"},"description":"Sample-level data."},"analyte_summary":{"type":"array","items":{"$ref":"#/components/schemas/AnalyteSummary"},"description":"Per-analyte counts across samples."},"summary_text":{"type":"string","description":"The factual summary paragraph shown on the result page."},"exposure":{"$ref":"#/components/schemas/Exposure"},"dataset":{"type":"object","description":"Dataset this record belongs to.","additionalProperties":false,"required":["version","license_url","citation","catalog_url"],"properties":{"version":{"type":"string","description":"Dataset version."},"license_url":{"type":"string","description":"License.","format":"uri"},"citation":{"type":"string","description":"Preferred citation."},"catalog_url":{"type":"string","description":"Dataset downloads (CSV/JSON).","format":"uri"}}},"caveat":{"type":"string","description":"Interpretation caveat to carry into answers."}}},"ResultResponse":{"type":"object","description":"Single result.","additionalProperties":false,"required":["data","dataset_version"],"properties":{"data":{"$ref":"#/components/schemas/ResultRecord"},"dataset_version":{"type":"string","description":"Dataset version."}}},"Analyte":{"type":"object","description":"One analyte.","additionalProperties":false,"required":["abbreviation","name","category","group","unit","panel","rating_contribution","guide_url"],"properties":{"abbreviation":{"type":"string","description":"Abbreviation used across the site and API. Null for panel phthalates the site names in full.","nullable":true},"name":{"type":"string","description":"Full name as it appears in measurements."},"category":{"type":"string","description":"Chemical category."},"group":{"type":"string","description":"Rating group.","enum":["phthalates","bisphenols","substitutes",null],"nullable":true},"unit":{"type":"string","description":"Unit.","enum":["ng/g"]},"panel":{"type":"string","description":"Laboratory panel.","enum":["primary_phthalate_panel","bisphenol_panel","supplemental_plasticizers",null],"nullable":true},"rating_contribution":{"type":"object","description":"How this analyte enters an exposure rating (null when it does not).","additionalProperties":false,"required":["rating_group","equivalency","factor"],"properties":{"rating_group":{"type":"string","description":"Group."},"equivalency":{"type":"string","description":"How it is combined."},"factor":{"type":"number","description":"Potency factor."}},"nullable":true},"guide_url":{"type":"string","description":"Explainer page.","format":"uri","nullable":true}}},"AnalytesResponse":{"type":"object","description":"Analyte reference data.","additionalProperties":false,"required":["unit_note","analytes","rating_groups","rating_thresholds_percent_of_tdi","lab_panels","methodology_url"],"properties":{"unit_note":{"type":"string","description":"Unit explanation."},"analytes":{"type":"array","items":{"$ref":"#/components/schemas/Analyte"},"description":"Analytes."},"rating_groups":{"type":"array","items":{"type":"object","description":"A rating group.","additionalProperties":false,"required":["group","tolerable_daily_intake_ng_per_kg_bw","basis"],"properties":{"group":{"type":"string","description":"Group."},"tolerable_daily_intake_ng_per_kg_bw":{"type":"number","description":"TDI."},"basis":{"type":"string","description":"Where the TDI and formula come from."}}},"description":"Rating groups."},"rating_thresholds_percent_of_tdi":{"type":"object","description":"Cut-offs.","additionalProperties":false,"required":["low_below","medium_below","high_at_or_above"],"properties":{"low_below":{"type":"number","description":"%"},"medium_below":{"type":"number","description":"%"},"high_at_or_above":{"type":"number","description":"%"}}},"lab_panels":{"type":"object","description":"Panels as documented in the methodology.","additionalProperties":false,"required":["primary_phthalate_panel","bisphenol_panel","supplemental_plasticizers"],"properties":{"primary_phthalate_panel":{"type":"array","items":{"type":"string"},"description":"Analytes."},"bisphenol_panel":{"type":"array","items":{"type":"string"},"description":"Analytes."},"supplemental_plasticizers":{"type":"array","items":{"type":"string"},"description":"Analytes."}}},"methodology_url":{"type":"string","description":"Methodology.","format":"uri"}}},"ContributionTerms":{"type":"object","description":"Terms to show before a person contributes.","additionalProperties":false,"required":["seller","refund_policy","what_it_funds","terms_url"],"properties":{"seller":{"type":"string","description":"Merchant of record."},"refund_policy":{"type":"string","description":"Refund rules."},"what_it_funds":{"type":"string","description":"What the money pays for."},"terms_url":{"type":"string","description":"Full terms.","format":"uri"}}},"CrowdfundItem":{"type":"object","description":"A product being (or previously) crowdfunded for testing.","additionalProperties":false,"required":["id","product","status","accepting_contributions","currency","funding_goal","amount_funded","amount_remaining","percent_funded","contributor_count","contribution","product_page_url","api_url","result_id","result_url","result_api_url"],"properties":{"id":{"type":"string","pattern":"^[1-9][0-9]{0,9}$","description":"Crowdfunding product id."},"product":{"type":"object","description":"Product.","additionalProperties":false,"required":["name","brand","category","image_url","retailer_url"],"properties":{"name":{"type":"string","description":"Name."},"brand":{"type":"string","description":"Brand.","nullable":true},"category":{"type":"string","description":"Category.","nullable":true},"image_url":{"type":"string","description":"Photo.","format":"uri","nullable":true},"retailer_url":{"type":"string","description":"Where it is sold.","format":"uri","nullable":true}}},"status":{"type":"string","description":"accepting_contributions, funded (goal met, awaiting lab), testing_in_progress, or tested.","enum":["accepting_contributions","funded","testing_in_progress","tested"]},"accepting_contributions":{"type":"boolean","description":"Whether a contribution checkout can be created now."},"currency":{"type":"string","description":"Currency.","enum":["USD"]},"funding_goal":{"type":"number","description":"Goal, USD."},"amount_funded":{"type":"number","description":"Raised so far, USD."},"amount_remaining":{"type":"number","description":"Still needed, USD (0 unless accepting contributions)."},"percent_funded":{"type":"number","description":"Percent of goal, 0–100.","nullable":true},"contributor_count":{"type":"integer","description":"Direct contributors."},"contribution":{"type":"object","description":"Allowed contribution range; null when not accepting.","additionalProperties":false,"required":["minimum","maximum","increments"],"properties":{"minimum":{"type":"number","description":"Smallest allowed contribution, USD."},"maximum":{"type":"number","description":"Largest allowed contribution, USD."},"increments":{"type":"string","description":"Allowed granularity."}},"nullable":true},"product_page_url":{"type":"string","description":"Public page where a person can also contribute.","format":"uri"},"api_url":{"type":"string","description":"This item.","format":"uri"},"result_id":{"type":"string","description":"Result id once tested.","nullable":true},"result_url":{"type":"string","description":"Result page once tested.","format":"uri","nullable":true},"result_api_url":{"type":"string","description":"Result API URL once tested.","format":"uri","nullable":true}}},"CrowdfundListResponse":{"type":"object","description":"Crowdfunding list.","additionalProperties":false,"required":["data","pagination","contribution_terms"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CrowdfundItem"},"description":"Products."},"pagination":{"$ref":"#/components/schemas/Pagination"},"contribution_terms":{"$ref":"#/components/schemas/ContributionTerms"}}},"CrowdfundResponse":{"type":"object","description":"One crowdfunding product.","additionalProperties":false,"required":["data","contribution_terms"],"properties":{"data":{"$ref":"#/components/schemas/CrowdfundItem"},"contribution_terms":{"$ref":"#/components/schemas/ContributionTerms"}}},"ContributionCheckoutRequest":{"type":"object","description":"Contribution request.","additionalProperties":false,"required":["amount"],"properties":{"amount":{"type":"integer","description":"Whole US dollars, within the product’s contribution range.","minimum":1,"maximum":1995}}},"ContributionCheckout":{"type":"object","description":"A created contribution checkout.","additionalProperties":false,"required":["checkout_id","checkout_url","status","requires_user_completion","amount","currency","expires_at","product","terms","next_step"],"properties":{"checkout_id":{"type":"string","description":"Checkout id for GET /checkouts/{checkout_id}."},"checkout_url":{"type":"string","description":"Secure Stripe-hosted payment page for the person to open.","format":"uri"},"status":{"type":"string","description":"Always awaiting_user_payment on creation.","enum":["awaiting_user_payment"]},"requires_user_completion":{"type":"boolean","description":"Always true: nothing is charged until the person pays on checkout_url."},"amount":{"type":"number","description":"USD."},"currency":{"type":"string","description":"Currency.","enum":["USD"]},"expires_at":{"type":"string","description":"When checkout_url stops working.","format":"date-time","nullable":true},"product":{"type":"object","description":"Product being funded.","additionalProperties":false,"required":["id","name","brand","amount_remaining_before_contribution","product_page_url"],"properties":{"id":{"type":"string","description":"Product id."},"name":{"type":"string","description":"Name."},"brand":{"type":"string","description":"Brand.","nullable":true},"amount_remaining_before_contribution":{"type":"number","description":"USD still needed before this contribution."},"product_page_url":{"type":"string","description":"Product page.","format":"uri"}}},"terms":{"$ref":"#/components/schemas/ContributionTerms"},"next_step":{"type":"string","description":"What to do next."}}},"TestingTerms":{"type":"object","description":"Terms to show before a person orders testing.","additionalProperties":false,"required":["seller","turnaround","refund_policy","publication","terms_url"],"properties":{"seller":{"type":"string","description":"Merchant of record."},"turnaround":{"type":"string","description":"Expected timing."},"refund_policy":{"type":"string","description":"Refund rules."},"publication":{"type":"string","description":"Whether results are published."},"terms_url":{"type":"string","description":"Full terms.","format":"uri"}}},"TestingTier":{"type":"object","description":"A direct testing package.","additionalProperties":false,"required":["id","name","price","currency","products_included","samples_per_product","lab_tests_total","panels","best_for"],"properties":{"id":{"type":"string","description":"Tier id.","enum":["single","kitchen","pantry"]},"name":{"type":"string","description":"Name."},"price":{"type":"number","description":"Total price, USD."},"currency":{"type":"string","description":"Currency.","enum":["USD"]},"products_included":{"type":"integer","description":"Products you may submit."},"samples_per_product":{"type":"integer","description":"Retail samples tested per product."},"lab_tests_total":{"type":"integer","description":"Total lab tests."},"panels":{"type":"array","items":{"type":"string"},"description":"Chemical panels."},"best_for":{"type":"string","description":"Who it suits.","nullable":true}}},"TestingTiersResponse":{"type":"object","description":"Testing packages.","additionalProperties":false,"required":["data","terms","order_page_url","service_page_url"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TestingTier"},"description":"Tiers."},"terms":{"$ref":"#/components/schemas/TestingTerms"},"order_page_url":{"type":"string","description":"Order page on the site.","format":"uri"},"service_page_url":{"type":"string","description":"Service description.","format":"uri"}}},"TestingCheckoutRequest":{"type":"object","description":"Testing order request.","additionalProperties":false,"required":["tier","products"],"properties":{"tier":{"type":"string","description":"Tier id from GET /testing/tiers.","enum":["single","kitchen","pantry"]},"products":{"type":"array","items":{"type":"object","description":"A product to test.","additionalProperties":false,"required":["url"],"properties":{"url":{"type":"string","description":"Public http(s) product page.","format":"uri","maxLength":2048},"notes":{"type":"string","description":"Optional size/variant hint.","maxLength":500}}},"description":"Between 1 and the package’s product count.","minItems":1,"maxItems":10}}},"TestingCheckout":{"type":"object","description":"A created testing checkout.","additionalProperties":false,"required":["checkout_id","checkout_url","status","requires_user_completion","amount","currency","expires_at","tier","products_submitted","terms","next_step"],"properties":{"checkout_id":{"type":"string","description":"Checkout id for GET /checkouts/{checkout_id}."},"checkout_url":{"type":"string","description":"Secure Stripe-hosted payment page for the person to open.","format":"uri"},"status":{"type":"string","description":"Always awaiting_user_payment on creation.","enum":["awaiting_user_payment"]},"requires_user_completion":{"type":"boolean","description":"Always true."},"amount":{"type":"number","description":"USD."},"currency":{"type":"string","description":"Currency.","enum":["USD"]},"expires_at":{"type":"string","description":"When checkout_url stops working.","format":"date-time","nullable":true},"tier":{"$ref":"#/components/schemas/TestingTier"},"products_submitted":{"type":"array","items":{"type":"object","description":"Submitted product.","additionalProperties":false,"required":["url","notes"],"properties":{"url":{"type":"string","description":"URL."},"notes":{"type":"string","description":"Notes (may be empty)."}}},"description":"Products saved with the order."},"terms":{"$ref":"#/components/schemas/TestingTerms"},"next_step":{"type":"string","description":"What to do next."}}},"CheckoutStatus":{"type":"object","description":"Checkout outcome. Contains no payer details.","additionalProperties":false,"required":["checkout_id","kind","status","amount","currency","created_at","expires_at","checkout_url","receipt","support_url"],"properties":{"checkout_id":{"type":"string","description":"Checkout id."},"kind":{"type":"string","description":"What is being bought.","enum":["crowdfund_contribution","testing_order"]},"status":{"type":"string","description":"awaiting_user_payment, paid, payment_processing, or expired. Only `paid` means payment succeeded.","enum":["awaiting_user_payment","paid","payment_processing","expired"]},"amount":{"type":"number","description":"USD."},"currency":{"type":"string","description":"Currency."},"created_at":{"type":"string","description":"Created.","format":"date-time","nullable":true},"expires_at":{"type":"string","description":"Expires.","format":"date-time","nullable":true},"checkout_url":{"type":"string","description":"Still-usable payment page (only while awaiting payment).","format":"uri","nullable":true},"product_id":{"type":"string","description":"Crowdfunding product id (contributions).","nullable":true},"tier_id":{"type":"string","description":"Tier id (testing orders)."},"receipt":{"type":"string","description":"Where the receipt goes once paid.","nullable":true},"support_url":{"type":"string","description":"Support for payment questions, disputes, and refunds.","format":"uri"}}}}}}
