FormulaSignal Signals API
Confirmed, reviewed changes and the release feed.
Operations 2
GET
/api/v1/signals/{signal_id}
Explain one approved FormulaSignal Signal: a confirmed, reviewed change between…
#
Confirmed, reviewed changes and the release feed.
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-signals-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 Signals 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 signals 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: Signals
description: Confirmed, reviewed changes and the release feed.
paths:
/api/v1/signals/{signal_id}:
get:
tags:
- Signals
operationId: explain_signal
summary: 'Explain one approved FormulaSignal Signal: a confirmed, reviewed change between…'
description: 'Explain one approved FormulaSignal Signal: a confirmed, reviewed change between two comparable product states.
**When to use.** Use when a product record or history response named a signal_id and you need the before state, the after state, the observation window, and the evidence.
**What it cannot provide.** It has no access to unreviewed candidates, review deliberation, or the internal review queue. A signal_id that is not approved returns not found.
Requires the `signals:read` scope.
Each distinct signal named in a successful answer spends one unit of the key''s daily signal budget. Asking twice about the same one costs once.'
security:
- apiKey: []
parameters:
- name: signal_id
in: path
required: true
description: The signal id.
schema:
type: string
- $ref: '#/components/parameters/Origin'
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: signals:read
x-formulasignal-entity-kind: signal
x-formulasignal-mcp-tool: signals.explain
x-formulasignal-mutates: false
servers:
- url: https://formulasignal.com
/api/v1/signals:
get:
tags:
- Signals
operationId: list_signal_changes
summary: Return approved FormulaSignal Signals in release order, newest first, for…
description: 'Return approved FormulaSignal Signals in release order, newest first, for incremental polling.
**When to use.** Use to keep a system in step with the Record. Pass `since` with the release date you last saw, or follow `next_cursor`, and filter by product, brand, dimension or category.
**What it cannot provide.** It returns no unreviewed candidate and no change event that fails the publication gate, and it is not an export of the product table.
Requires the `signals:read` scope.
Each distinct signal named in a successful answer spends one unit of the key''s daily signal budget. Asking twice about the same one costs once.'
security:
- apiKey: []
parameters:
- name: since
in: query
required: false
schema:
type: string
format: date
- name: until
in: query
required: false
schema:
type: string
format: date
- name: product_id
in: query
required: false
schema:
type: string
- name: brand
in: query
required: false
schema:
type: string
- name: dimension
in: query
required: false
schema:
type: string
- name: category
in: query
required: false
schema:
type: string
- name: limit
in: query
required: false
schema:
type: integer
maximum: 25
- name: cursor
in: query
required: false
schema:
type: string
- $ref: '#/components/parameters/Origin'
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: signals:read
x-formulasignal-entity-kind: signal
x-formulasignal-mcp-tool: signals.list
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)