Docs
A small, boring REST API. JSON in, JSON out, keyed auth, cursor pagination. You should be pulling records within about five minutes.
Authentication
Every request carries a bearer token. Generate keys in your dashboard under Settings → API keys. A sandbox key is issued with your trial, so you can integrate before you pay.
curl https://api.formationsfeed.com/v1/formations \ -H "Authorization: Bearer ff_live_your_key_here"
Keep keys server-side. A key grants access to every state on your plan. If one leaks, roll it from the dashboard — old keys stop working immediately, and nothing else about your feed changes.
Quickstart
Pull yesterday’s Florida filings. This is the whole thing:
# Yesterday's Florida filings curl -G https://api.formationsfeed.com/v1/formations \ -H "Authorization: Bearer $FF_KEY" \ -d state=FL \ -d filed_date=2026-07-16 # Everything new since your last sync, any state on your plan curl -G https://api.formationsfeed.com/v1/formations \ -H "Authorization: Bearer $FF_KEY" \ -d since=2026-07-15T00:00:00Z \ -d limit=100
Errors & limits
Standard HTTP status codes. Errors return a JSON body with a stable code you can branch on — don’t parse the message, it may change.
| Status | Code | What it means |
|---|---|---|
400 | invalid_request | A parameter is missing or malformed. The message names the field. |
401 | bad_key | Missing, malformed, or rolled API key. |
403 | state_not_on_plan | You asked for a state you aren’t subscribed to. Add it in the dashboard. |
404 | not_found | No entity with that ID, on your plan. |
429 | rate_limited | Slow down. Check the Retry-After header. |
503 | registry_down | An upstream registry is unavailable. The batch will backfill. |
Rate limits are 10 requests/second on Feed + API. Every response carries X-RateLimit-Remaining.
List formations
GET/v1/formations
The endpoint you’ll live in. Returns new formations, newest first, filtered by any combination below.
| Parameter | Type | Description |
|---|---|---|
state | string | Two-letter code. Repeat for several: ?state=FL&state=TX. Omit for every state on your plan. |
filed_date | date | Exact filing date, YYYY-MM-DD. |
filed_after | date | Inclusive lower bound. |
filed_before | date | Inclusive upper bound. |
since | timestamp | ISO 8601. Everything added to the feed after this moment — the right way to sync. |
entity_type | string | llc_domestic, corp_domestic, lp, llp, nonprofit, foreign. |
agent | string | Registered agent name, normalized match. |
city | string | Principal address city. |
zip | string | Principal address ZIP. |
has_email | boolean | Only records carrying a verified email. |
limit | integer | 1–500. Default 50. |
cursor | string | From next_cursor. See pagination. |
curl -G https://api.formationsfeed.com/v1/formations \ -H "Authorization: Bearer $FF_KEY" \ -d state=FL -d entity_type=llc_domestic \ -d has_email=true -d limit=50
Get one entity
GET/v1/formations/{entity_id}
Fetch a single record by its entity_id. Useful for re-checking status on something you pulled weeks ago.
curl https://api.formationsfeed.com/v1/formations/FL-L26000418822 \ -H "Authorization: Bearer $FF_KEY"
List states
GET/v1/states
Which states are live, which are on your plan, and when each last published. Poll this rather than hard-coding a coverage list — we add registries continuously.
{
"data": [
{ "state": "FL", "live": true, "on_plan": true,
"last_published": "2026-07-17T06:12:04Z",
"fields": { "officers": true, "principal_address": true } },
{ "state": "TX", "live": true, "on_plan": true,
"last_published": "2026-07-17T06:30:51Z" }
]
}
Webhooks
POST/v1/webhooks
Register an endpoint and we’ll post each morning’s batch the moment it clears validation, so you never poll.
curl https://api.formationsfeed.com/v1/webhooks \ -H "Authorization: Bearer $FF_KEY" \ -d url=https://yourapp.com/hooks/ff \ -d events=batch.ready
Events: batch.ready when a state’s daily batch is available, batch.late when a registry hasn’t published on time, state.added when new coverage goes live.
Verify the signature. Every payload carries an X-FF-Signature header — an HMAC-SHA256 of the raw body using your webhook secret. Compare it before you trust the contents, and use a constant-time comparison.
The formation object
Identical shape in every state. Fields we don’t have come back null — never guessed, never filled with a placeholder. Full fill rates are on the data page.
{
"entity_id": "FL-L26000418822",
"name": "HARBOR LINE LOGISTICS LLC",
"name_normalized": "harbor line logistics",
"entity_type": "llc_domestic",
"state": "FL",
"filed_date": "2026-07-16",
"status": "active",
"registered_agent": {
"name": "MERIDIAN AGENTS INC.",
"address": "1201 Brickell Ave Ste 400",
"city": "Miami", "state": "FL", "zip": "33131",
"is_commercial": true
},
"officers": [
{ "name": "Dana Ruiz", "title": "MGR", "address": null }
],
"principal_address": "84 Coral Way, Miami, FL 33145",
"contact": {
"email": "druiz@harborlinelog.co",
"email_confidence": 0.91,
"phone": "+13055550148",
"website": null
},
"added_at": "2026-07-17T06:12:04Z"
}
Sort on added_at, not filed_date — a registry occasionally publishes late, so a record filed Tuesday can land in Thursday’s batch. Syncing on since handles this for you.
Pagination
Cursor-based. Keep calling with the returned next_cursor until has_more is false. Cursors are opaque and expire after 24 hours.
{
"data": [ /* … 50 formation objects … */ ],
"has_more": true,
"next_cursor": "eyJvIjoxNTAsInMiOiJGTCJ9"
}
# Page through a whole day
cursor=""
while :; do
resp=$(curl -sG https://api.formationsfeed.com/v1/formations \
-H "Authorization: Bearer $FF_KEY" \
-d state=FL -d filed_date=2026-07-16 -d limit=500 \
${cursor:+-d cursor=$cursor})
echo "$resp" | jq -c '.data[]'
[ "$(echo "$resp" | jq -r '.has_more')" = "true" ] || break
cursor=$(echo "$resp" | jq -r '.next_cursor')
done
Sandbox keys come with the trial.
Two days free, no card. Integrate against real records before you pay for anything.
Start free trial →