Observations
GET /v1/observations returns time-series rows for an indicator and optional region. This endpoint requires an API key or x402 payment.
Request
curl "https://api.creativeforesight.io/v1/observations?indicator=UNRATE®ion=US&limit=12" \
-H "Authorization: Bearer $CF_API_KEY"Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
indicator | string | Yes | Indicator code, such as UNRATE or cf:labor_composite. |
source | string | No | Source slug for duplicate provider codes. |
region | string | No | Region code. Uses the indicator default region when omitted. |
start_date | date | No | Inclusive lower date bound. |
end_date | date | No | Inclusive upper date bound. |
include_revisions | boolean | No | Include revision history instead of only latest revisions. Defaults to false. Cannot combine with transform. |
transform | string | No | Query-time transform: yoy, mom, qoq, index:YYYY-MM-DD, or log. See Transforms. |
frequency | string | No | Downsample to monthly, quarterly, or annual calendar buckets. See Resampling. |
resample_method | string | No | Bucket aggregation: mean (default), last, or sum. Requires frequency. |
limit | integer | No | Page size, 0 to 1000. Defaults to 100. |
offset | integer | No | Page offset. Defaults to 0. |
order | asc or desc | No | Sort order. Defaults to desc. |
Observation object
| Field | Type | Description |
|---|---|---|
date | date | Observation date. |
value | number or null | Numeric value. |
indicator | string | Indicator code. |
region | string | Region code. |
unit | string or null | Unit label. |
source | string or null | Source slug. |
source_updated_at | date-time or null | Provider update timestamp. |
is_preliminary | boolean | Whether the row is preliminary. |
revision | integer | Revision number. |
Response
{
"data": [
{
"date": "2026-05-01",
"value": 4.2,
"indicator": "UNRATE",
"region": "US",
"unit": "percent",
"source": "fred",
"source_updated_at": "2026-06-06T12:00:00.000Z",
"is_preliminary": false,
"revision": 0
}
],
"meta": {
"indicator": "UNRATE",
"region": "US",
"unit": "percent",
"total": 1,
"limit": 12,
"offset": 0
}
}Transforms
Add transform= to get derived values without post-processing:
| Transform | Result | Unit |
|---|---|---|
yoy | Percent change vs the same period one year earlier. | percent |
mom | Percent change vs the previous period. | percent |
qoq | Percent change vs the previous quarter (quarterly series only). | percent |
index:YYYY-MM-DD | Values rescaled so the base date equals 100. | index (BASE=100) |
log | Natural log of each value. | log(unit) |
curl "https://api.creativeforesight.io/v1/observations?indicator=UNRATE®ion=US&transform=yoy" \
-H "Authorization: Bearer cf_live_REPLACE_ME"Percent changes compare calendar periods, not row positions: a row whose comparator period is missing from the series — or falls outside the requested date window or page — returns null rather than a misleading number. The response meta.unit reflects the transform and meta.transform echoes what was applied. yoy is unavailable on daily series at their native frequency — add frequency=monthly to get monthly year-over-year from daily data. transform cannot combine with include_revisions.
Resampling
Add frequency= to downsample a series to calendar buckets before any transform runs:
curl "https://api.creativeforesight.io/v1/observations?indicator=DGS2&frequency=monthly&transform=yoy" \
-H "Authorization: Bearer cf_live_REPLACE_ME"Buckets are labeled by their calendar start date (2026-07-01 for July, Q3, or 2026) and aggregated with resample_method — mean (default), last, or sum. Resampled rows are synthetic aggregates carrying only period_start, period_end, period_type, and value; per-row fields like revision don’t survive aggregation. Requesting a series’ own native frequency returns it unchanged.
Two honesty rules protect you from partial buckets: a trailing bucket the source hasn’t finished (say, July from daily data that ends July 14th) is dropped rather than served as if complete, and when a page is truncated by limit/offset, the buckets at the truncated edges are dropped because they may be missing source rows. meta.resample reports the method, the source and target frequencies, and both kinds of drops. Prefer start_date/end_date windows over pagination when resampling — a full window has no truncated edges.
Paying per request (x402 or Lightning)
Unauthenticated calls return HTTP 402 with two ways to pay:
- x402 (USDC on Base): the JSON body’s
acceptsdescribes the payment; retry withX-PAYMENT. - Lightning (L402): the
WWW-Authenticateheader carries a macaroon and a bolt11 invoice. Pay the invoice from any Lightning wallet, then retry the same request withAuthorization: L402 <macaroon>:<preimage>(your wallet shows the preimage after paying). The macaroon covers the requested indicator for 10 minutes.
Either proof settles the request.
Errors
| Status | Code | Meaning |
|---|---|---|
400 | INVALID_PARAMETER | Required or optional query parameters are invalid. |
401 | INVALID_API_KEY | The request did not include a valid API key and was not a settled x402 request. |
404 | RESOURCE_NOT_FOUND | The requested indicator or region was not found. |
429 | RATE_LIMIT_EXCEEDED | The API key exceeded its one-minute request limit. |
500 | INTERNAL_SERVER_ERROR | Observation data could not be loaded. |