FormulaSignal Context API
Research and regulatory context for declared ingredients.
Research and regulatory context for declared ingredients.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/formulasignal-com:formulasignal-com-context-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Formulasignal Com Context API
version: 1.0.0
contact:
name: FormulaSignal
url: https://formulasignal.com/developers
email: hello@formulasignal.com
license:
name: Commercial. Redistribution depends on your plan.
description: 'Operations tagged context across 2 of this provider''s published API definitions: openapi.json, formulasignal-com-openapi.json. Each path carries the servers of the definition it was published in.'
servers:
- url: https://formulasignal.com
security:
- apiKey: []
tags:
- name: Context
description: Research and regulatory context for declared ingredients.
paths:
/api/v1/research/context:
post:
tags:
- Context
operationId: get_research_context
summary: Return the dose range used in the selected evidence set for named ingredients…
description: 'Return the dose range used in the selected evidence set for named ingredients, with the citation and its limitations.
**When to use.** Use to place a declared amount against published work. Pass ingredient ids, or a product_id to read the ingredients that product declares.
**What it cannot provide.** A dose comparison is not evidence of effectiveness, safety, or suitability for any person, and the response says so on every record.
Requires the `research:read` scope.
Each distinct ingredient named in a successful answer spends one unit of the key''s daily ingredient budget. Asking twice about the same one costs once.'
security:
- apiKey: []
parameters:
- $ref: '#/components/parameters/Origin'
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
properties:
ingredient_ids:
type: array
items:
type: string
maxItems: 5
product_id:
type: string
responses:
'200':
$ref: '#/components/responses/Envelope'
'400':
$ref: '#/components/responses/Refused'
'401':
$ref: '#/components/responses/Refused'
'403':
$ref: '#/components/responses/Refused'
'404':
$ref: '#/components/responses/Refused'
'405':
$ref: '#/components/responses/Refused'
'413':
$ref: '#/components/responses/Refused'
'429':
$ref: '#/components/responses/Refused'
'500':
$ref: '#/components/responses/Refused'
x-formulasignal-scope: research:read
x-formulasignal-entity-kind: ingredient
x-formulasignal-mcp-tool: ingredients.research
x-formulasignal-mutates: false
servers:
- url: https://formulasignal.com
/api/v1/regulatory/context:
post:
tags:
- Context
operationId: get_regulatory_context
summary: Return regulatory records and documented cautions for named ingredients, each…
description: 'Return regulatory records and documented cautions for named ingredients, each carrying the class of record it actually is.
**When to use.** Use when you need to know what filings, advisories, label warnings, interactions, or enforcement actions FormulaSignal holds for an ingredient.
**What it cannot provide.** A regulatory filing is never returned as an approval, a warning, or a finding of harm. Nothing here is medical clearance.
Requires the `regulatory:read` scope.
Each distinct ingredient named in a successful answer spends one unit of the key''s daily ingredient budget. Asking twice about the same one costs once.'
security:
- apiKey: []
parameters:
- $ref: '#/components/parameters/Origin'
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
properties:
ingredient_ids:
type: array
items:
type: string
maxItems: 5
product_id:
type: string
responses:
'200':
$ref: '#/components/responses/Envelope'
'400':
$ref: '#/components/responses/Refused'
'401':
$ref: '#/components/responses/Refused'
'403':
$ref: '#/components/responses/Refused'
'404':
$ref: '#/components/responses/Refused'
'405':
$ref: '#/components/responses/Refused'
'413':
$ref: '#/components/responses/Refused'
'429':
$ref: '#/components/responses/Refused'
'500':
$ref: '#/components/responses/Refused'
x-formulasignal-scope: regulatory:read
x-formulasignal-entity-kind: ingredient
x-formulasignal-mcp-tool: ingredients.regulatory
x-formulasignal-mutates: false
servers:
- url: https://formulasignal.com
components:
schemas:
Confidence:
type: object
required:
- level
- explanation
properties:
level:
type: string
enum:
- high
- moderate
- low
- none
description: Evidence quality. Never how certain the writing sounds, and never "certain".
explanation:
type: string
VerifiedFact:
type: object
required:
- field
properties:
field:
type: string
value:
$ref: '#/components/schemas/FactValue'
before:
$ref: '#/components/schemas/FactValue'
after:
$ref: '#/components/schemas/FactValue'
confirmed:
type: boolean
Envelope:
type: object
required:
- request_id
- capability
- status
- summary
- verified_facts
- commercial_facts
- calculations
- research_context
- regulatory_records
- documented_cautions
- limitations
- source_references
- confidence
- record_version
- record_as_of
- record_timestamp
- methodology_version
- disclaimer
properties:
request_id:
type: string
pattern: ^req_[0-9a-f]{24}$
capability:
type: string
status:
type: string
enum:
- supported
- partial
- stale
- under_review
- ambiguous
- unsupported
- refused
description: What kind of answer this is. Every value except `supported` is still a real answer about the Record rather than a fact about the product.
product:
oneOf:
- $ref: '#/components/schemas/ProductIdentity'
- type: 'null'
products:
type: array
items:
$ref: '#/components/schemas/ProductIdentity'
candidates:
type: array
description: Present when `status` is `ambiguous`. Pick one; never assume the first.
items:
$ref: '#/components/schemas/ProductIdentity'
summary:
type: string
description: One sentence saying what kind of answer this is.
history:
type: array
items:
$ref: '#/components/schemas/HistoryState'
signals:
type: array
items:
type: object
comparison:
type: object
coverage:
type: object
ledger:
type: object
watch:
type: object
next_cursor:
oneOf:
- type: string
- type: 'null'
verified_facts:
type: array
description: What a captured source literally declares. A declared fact.
items:
$ref: '#/components/schemas/VerifiedFact'
commercial_facts:
type: array
description: Dated commercial observations, each with its price basis. An observation.
items:
$ref: '#/components/schemas/CommercialFact'
calculations:
type: array
description: Deterministic arithmetic FormulaSignal performed, with the operands to redo it. Never a manufacturer figure.
items:
$ref: '#/components/schemas/Calculation'
research_context:
type: array
items:
$ref: '#/components/schemas/ResearchContext'
regulatory_records:
type: array
items:
$ref: '#/components/schemas/RegulatoryRecord'
documented_cautions:
type: array
items:
$ref: '#/components/schemas/RegulatoryRecord'
limitations:
type: array
description: Always present, including when empty. Read it.
items:
type: string
source_references:
type: array
items:
$ref: '#/components/schemas/SourceReference'
confidence:
$ref: '#/components/schemas/Confidence'
record_version:
type: string
description: The Record data-state id. Two answers sharing it came from one committed state.
record_as_of:
type: string
format: date
record_timestamp:
type: string
format: date-time
description: The newest dated observation the Record holds. Deliberately not "now".
methodology_version:
type: string
examples:
- record-v1
disclaimer:
type: string
examples:
- FormulaSignal provides evidence-backed supplement information and does not provide individualized medical advice.
HistoryState:
type: object
properties:
observed_at:
oneOf:
- type: string
format: date
- type: 'null'
role:
type: string
enum:
- CURRENT
- CHANGE_BEFORE
- CHANGE_AFTER
- RICHEST_PRIOR_PANEL
classes:
type: array
items:
type: string
enum:
- METADATA_ONLY
- LABEL_BEARING
- FORMULA_COMPARABLE
- PRICE_COMPARABLE
declared_components:
oneOf:
- type: integer
- type: 'null'
declared_with_amounts:
oneOf:
- type: integer
- type: 'null'
flavor:
oneOf:
- type: string
- type: 'null'
source:
oneOf:
- $ref: '#/components/schemas/SourceReference'
- type: 'null'
ResearchContext:
type: object
required:
- normalized_ingredient
- comparison
- comparison_statement
- limitations
properties:
normalized_ingredient:
type: string
ingredient_form:
oneOf:
- type: string
- type: 'null'
declared_amount_mg:
oneOf:
- type: number
- type: 'null'
selected_low_mg:
oneOf:
- type: number
- type: 'null'
selected_high_mg:
oneOf:
- type: number
description: Null on both bounds means FormulaSignal found no studied range. That is a finding, not a gap.
- type: 'null'
dose_basis:
oneOf:
- type: string
- type: 'null'
evidence_type:
oneOf:
- type: string
enum:
- A1
- A2
- A3
- A4
description: A1 government or institutional, A2 peer-reviewed, A3 trademark holder, A4 other secondary.
- type: 'null'
citation:
oneOf:
- type: string
- type: 'null'
citation_url:
oneOf:
- type: string
format: uri
- type: 'null'
comparison:
type: string
enum:
- INSIDE
- BELOW
- ABOVE
- NO_RANGE
- NOT_DECLARED
- NO_PRODUCT_CONTEXT
comparison_statement:
type: string
limitations:
type: array
items:
type: string
SourceReference:
type: object
required:
- source_id
- title
- url
- observed_at
- source_type
properties:
source_id:
type: string
description: Derived and opaque. Stable for a given URL and observation date.
title:
type: string
url:
oneOf:
- type: string
format: uri
- type: 'null'
observed_at:
oneOf:
- type: string
format: date
description: When FormulaSignal read the source. Not a manufacture date.
- type: 'null'
source_type:
type: string
enum:
- first_party_current
- first_party_archived
- regulatory_filing
- retailer_listing
- secondary_coverage
- distributor_listing
- authoritative_reference
- peer_reviewed_reference
- interested_party_reference
- secondary_reference
- other
evidence_class:
oneOf:
- type: string
description: Only P1, P2 and R1 support a CONFIRMED product-state label.
- type: 'null'
ErrorEnvelope:
type: object
required:
- request_id
- capability
- status
- error
- disclaimer
properties:
request_id:
type: string
capability:
type: string
status:
type: string
enum:
- refused
error:
type: object
required:
- code
- message
properties:
code:
type: string
enum:
- invalid_credentials
- insufficient_scope
- plan_entitlement_required
- key_suspended
- rate_limited
- extraction_limit_reached
- invalid_request
- payload_too_large
- not_found
- unsupported_capability
- method_not_allowed
- internal_error
description: A stable machine code. Branch on this, never on `message`, which is written for a person and may be reworded.
message:
type: string
details:
type: object
documentation:
type: string
format: uri
limitations:
type: array
items:
type: string
record_version:
type: string
record_as_of:
type: string
record_timestamp:
type: string
methodology_version:
type: string
disclaimer:
type: string
FactValue:
type: object
properties:
declared_name:
type: string
description: Exactly as printed or published by the brand.
normalized_ingredient:
type: string
amount:
oneOf:
- type: number
description: Null means the source named the ingredient without disclosing an amount. It never means zero.
- type: 'null'
unit:
oneOf:
- type: string
- type: 'null'
ProductIdentity:
type: object
required:
- product_id
- brand
- name
- variant
properties:
product_id:
type: string
brand:
type: string
name:
type: string
variant:
oneOf:
- type: string
description: The observed flavor. Flavor is a version dimension, not a footnote.
- type: 'null'
Calculation:
type: object
required:
- field
- value
- unit
- method
- operands
properties:
field:
type: string
value:
oneOf:
- type: number
- type: 'null'
unit:
oneOf:
- type: string
- type: 'null'
method:
type: string
description: The arithmetic in words, so a reader can redo it.
operands:
type: array
items:
type: object
properties:
label:
type: string
value:
oneOf:
- type: number
- type: string
- type: 'null'
CommercialFact:
type: object
required:
- field
- value
- unit
- observed_at
properties:
field:
type: string
description: '`list_price` only where `price_basis` is LIST. Otherwise `observed_price`.'
value:
oneOf:
- type: number
- type: string
- type: 'null'
unit:
oneOf:
- type: string
- type: 'null'
price_basis:
oneOf:
- type: string
description: LIST, UNKNOWN, UNRECORDED, or the two recorded bases on a change row. Never inferred from the number.
- type: 'null'
observed_at:
oneOf:
- type: string
format: date
- type: 'null'
seller:
oneOf:
- type: string
- type: 'null'
RegulatoryRecord:
type: object
required:
- normalized_ingredient
- record_class
- statement
- evidence_type
- citation
- citation_url
- accessed_at
properties:
normalized_ingredient:
type: string
record_class:
type: string
enum:
- REGULATORY_RECORD
- LABEL_WARNING
- SAFETY_ADVISORY
- INTERACTION
- FDA_ACTION
description: REGULATORY_RECORD means a filing exists and says nothing about safety on its own. It appears in `regulatory_records`; the other four appear in `documented_cautions`. The two never share a heading.
statement:
type: string
evidence_type:
type: string
enum:
- A1
- A2
- A3
- A4
citation:
type: string
citation_url:
type: string
format: uri
accessed_at:
type: string
format: date
responses:
Refused:
description: A refusal, in the same envelope shape.
headers:
X-Request-Id:
schema:
type: string
Retry-After:
schema:
type: string
description: 'Seconds. Present on 429 only, for both `rate_limited` and `extraction_limit_reached`. On an extraction refusal, waiting is the only thing that helps: rewording the question or narrowing the window spends more of the same daily budget.'
Cache-Control:
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
Envelope:
description: A FormulaSignal answer. `status` says what kind of answer it is, and every value except `supported` is still a real answer about the Record rather than a fact about the product.
headers:
X-Request-Id:
schema:
type: string
description: Quote this if you contact support.
RateLimit-Limit:
schema:
type: string
RateLimit-Remaining:
schema:
type: string
RateLimit-Reset:
schema:
type: string
description: Seconds until the window resets.
RateLimit-Policy:
schema:
type: string
enum:
- minute
- hour
- day
Cache-Control:
schema:
type: string
description: Always `private, no-store, max-age=0, must-revalidate`. Every answer is entitlement-specific, so no shared cache may hold one.
content:
application/json:
schema:
$ref: '#/components/schemas/Envelope'
parameters:
Origin:
name: X-FormulaSignal-Origin
in: header
required: false
description: 'Optional attribution: `mcp` when the call comes from the FormulaSignal MCP server, `api` otherwise. Self-reported and recorded on the usage row so a customer can see which surface their traffic came from. Nothing reads it to decide what a caller may do, so a wrong value costs only the accuracy of that customer''s own reporting.'
schema:
type: string
enum:
- api
- mcp
securitySchemes:
apiKey:
type: http
scheme: bearer
bearerFormat: fsk_{environment}_{keyId}_{secret}
description: '`Authorization: Bearer fsk_live_...`. The header `X-FormulaSignal-Key: fsk_live_...`
is accepted as an alternative, because an MCP client configuration is a JSON file a
person edits by hand and "remember to write Bearer in front of it" is a support ticket.
The key is never accepted in a query string, so it cannot reach an access log or a
referrer header. Create one at https://formulasignal.com/developers/keys.'
externalDocs:
description: Developer documentation, quickstart and the evidence model.
url: https://formulasignal.com/developers
x-refined-from:
- openapi.json
- formulasignal-com-openapi.json
x-formulasignal-plans:
- id: free
label: Free
rate:
perMinute: 30
perHour: 500
perDay: 2000
daily_distinct_products: 15
daily_distinct_signals: 15
daily_distinct_ingredients: 25
self_service: true
commercial_redistribution: false
- id: trial
label: Trial
rate:
perMinute: 10
perHour: 100
perDay: 250
daily_distinct_products: 10
daily_distinct_signals: 10
daily_distinct_ingredients: 15
self_service: false
commercial_redistribution: false
- id: pro
label: Pro
rate:
perMinute: 30
perHour: 600
perDay: 3000
daily_distinct_products: 25
daily_distinct_signals: 25
daily_distinct_ingredients: 40
self_service: false
commercial_redistribution: false
- id: developer
label: Developer
rate:
perMinute: 60
perHour: 2000
perDay: 15000
daily_distinct_products: 40
daily_distinct_signals: 40
daily_distinct_ingredients: 80
self_service: false
commercial_redistribution: true
- id: enterprise
label: Enterprise
rate:
perMinute: 120
perHour: 6000
perDay: 60000
daily_distinct_products: null
daily_distinct_signals: null
daily_distinct_ingredients: null
self_service: false
commercial_redistribution: true
x-formulasignal-hard-limits:
queryChars: 300
identifierChars: 120
productsPerResponse: 25
answerStatements: 12
answerCharsPerStatement: 800
compareProducts: 3
sourceReferences: 12
sourceExcerptChars: 0
historyStates: 12
researchIngredients: 5
researchRecordsPerProduct: 12
snapshotWindowDays: 400
snapshotPageSize: 25
requestBytes: 8192
x-provenance:
method: harvested
publisher: FormulaSignal
source: https://formulasignal.com/openapi.json
note: split by refine-openapis from the provider's own published document, verified by fetch (openapi/_original/*.fetch.json);
stamped by stamp-openapi-provenance.py (roadmap#793)