Developers · quickstart
Economic data API: release calendar, published values and official news as JSON
How to call HELPERG’s public economic endpoints: the official release calendar of Eurostat, the ECB and the U.S. BLS, the newest published values per series, the current vintage of each reference period of a series with its revision count, and the publishers’ own news — as JSON, with no credential. The three kinds of time on a record, decimal values, computed figures, revisions, paging, limits and attribution per publisher.
Full API referenceOpenAPI 3.1 documentThe calendar these endpoints serve
Before the first request
- What is in it
- Official statistics only, from the publishers themselves: Eurostat, the European Central Bank and the U.S. Bureau of Labor Statistics, read on a schedule from their own calendars, data interfaces and news feeds. There is no forecast, consensus, estimate or commentary of any kind — by decision, not by omission — so no field for one exists; a value is either the publisher’s own figure or a change HELPERG computed from the publisher’s published inputs, and the record says which.
- Who may call it
- Anyone, today, without a credential: these routes are the public namespace
/api/public/v1, rate-limited per client and per route, with no service-level agreement. Additive fields may appear at any time; a breaking change would be a new version segment. Error codes are part of the contract; messages are not. - Whose data it is
- Each record names its publisher and carries the attribution it requires: BLS data is a U.S. Government work that asks to be credited, Eurostat’s is CC BY 4.0, the ECB’s may be reproduced with the ECB cited as source.
meta.attributionRecords[]lists every publisher behind a response with its licence;modifiedByHelpergsays whether HELPERG transformed anything (a computed change, a time-zone conversion) on the way.
The endpoints
GET /api/public/v1/economics/calendar- Scheduled official releases, grouped by day.
fromandtobound an inclusive window —YYYY-MM-DDintz, or ISO instants — defaulting to 7 days back and 31 ahead, at most 93 days.tzaddslocal.date,local.timeandlocal.timeZoneto each event while the UTC values stay.source,release,countryandcurrencytake up to 10 comma-separated tokens;regionandstatusare closed enumerations and an unknown value is refused, not ignored. Pages of 50, at most 200, continued withmeta.nextCursor. Shared cache: 300 seconds. GET /api/public/v1/economics/releases- The newest published value of each registered series, merged newest first.
seriesrestricts to up to 10 slugs;countryandregionfilter;perSeriesreturns up to 5 newest reference periods of each series, each with its current vintage — older periods, never older vintages of one period. Pages of 20, at most 100. Shared cache: 300 seconds. GET /api/public/v1/economics/observations- The current vintage of each reference period of one series, oldest period first — each with
revisionCount(vintages beyond the first),isRevisionandpreviousValue(the value the current vintage replaced). Earlier vintages stay in storage and are not served by any public endpoint.seriesis required — an unknown slug is 404, a registered series with nothing ingested on this deployment is 503 — andfrom/toare reference-period codes such as2026-08or2026-Q2, compared as strings. Pages of 100, at most 365. Shared cache: 600 seconds. GET /api/public/v1/news/events- The publishers’ own announcements, newest first: title, link to the publisher’s page, publication time, HELPERG’s classification and the calendar event, series and values it links to — with the evidence for each link.
from/tobound the publication window, defaulting to the last 366 days;source,releaseFamily,series,country,topicand the closedstate,linkStatusandtypeenumerations filter. Pages of 20, at most 100. Shared cache: 300 seconds. GET /api/public/v1/status- Domain-level status —
ok,degraded,unavailable,unknown— with when each feed last received data. Read it before relying on a value in an automated pipeline; cached 30 seconds.
Quickstart
curl
# Releases scheduled in a window, with local times in Berlin
curl -sS "https://helperg.com/api/public/v1/economics/calendar?from=2026-10-01&to=2026-10-31&tz=Europe/Berlin&source=eurostat-release-calendar"
# The newest published value of each registered series
curl -sS "https://helperg.com/api/public/v1/economics/releases?limit=50"
# The current vintage of each period of one series, oldest first
curl -sS "https://helperg.com/api/public/v1/economics/observations?series=us-cpi-headline-yoy&from=2025-01"
# Official publications of the last 30 days, newest first
curl -sS "https://helperg.com/api/public/v1/news/events?from=2026-09-17&limit=20"JavaScript
// Node.js 18+ or any runtime with fetch. Values are decimal STRINGS: parse with a decimal library, not Number().
const url = new URL("https://helperg.com/api/public/v1/economics/releases");
url.searchParams.set("series", "us-cpi-headline-yoy,ea-unemployment-rate-sa");
const { data, meta } = await (await fetch(url, { headers: { Accept: "application/json" } })).json();
for (const release of data.releases) {
const computed = release.derived ? ` (computed by HELPERG: ${release.derived.formula})` : "";
console.log(release.series.slug, release.referencePeriod.code, release.value, release.series.unit.code + computed);
}
console.log(meta.attributionRecords.map((a) => a.text).join(" "));Python
import json, urllib.request
from decimal import Decimal
url = "https://helperg.com/api/public/v1/economics/observations?series=us-cpi-headline-yoy&from=2026-01"
with urllib.request.urlopen(urllib.request.Request(url, headers={"Accept": "application/json"})) as r:
body = json.load(r)
for o in body["data"]["observations"]:
value = Decimal(o["value"]) if o["value"] is not None else None # exact, as published
print(o["referencePeriod"], value, "revisions:", o["revisionCount"], "replaced:", o["previousValue"])Three kinds of time on every record
Most mistakes with release data are mistakes about which clock a field reads. The records keep them apart and so should your code.
- The publisher’s time
- On a calendar event,
scheduledAt(UTC) withscheduledDate,timePrecisionand thesourceTimezonethe publisher announced it in; a date-only announcement haslocal.time: nullrather than an invented midnight. On a release or news item,publishedAtandsourceUpdatedAtare what the publisher states —nullwhen it states nothing, never a guess. - HELPERG’s time
ingestedAton a value andpublication.detectedAton an event are when HELPERG’s scheduled run recorded the fact. A gap between the publisher’s time and these is real — the runs are scheduled, not instant — and is shown, not hidden.- The index’s time, and the feed’s
meta.dataUpdatedAtmeans a different thing per route, and the name hides it. On the calendar it is the time the event index was last rebuilt; on observations it is the time the series index was last rebuilt; on releases it is the newest such rebuild among the series included in the answer. An index is rebuilt by every scheduled run that read its source, so this time moves on every scheduled run that rebuilt it, whether or not a value changed — an unchanged value does not mean an unchanged timestamp, and a moved timestamp does not mean a new value. Only the news feed is different: theremeta.dataUpdatedAtis the feed’scontentUpdatedAt, the newest change to a publication’s content, which a read that found nothing new leaves where it was.meta.generatedAtis merely when this response was built, andmeta.cachedwithmeta.cacheAgeSecondssays whether it came from HELPERG’s cache.meta.dataStatusisfresh,staleorunavailable, judged against yet another clock: on the calendar, the oldest successful source check among the calendar sources that have run —unavailablewhile any of them has run and never succeeded — on observations and releases the index time above, and on news the last successful read, inside the feed’s own freshness window. The calendar also lists each source inmeta.sourceChecksassourceId,lastSuccessAtandlatestCheck:successful,incomplete(the last run was partial or failed) ornot_recorded(never run on this deployment). That is the whole public shape; HELPERG’s internal failure counter is not published.- Status is evidence, not the clock
- A calendar event keeps the publisher’s
status(SCHEDULED,RESCHEDULED,PUBLISHED…) and addsderivedState: when the scheduled time has passed with no evidence of publication the state isPAST_DUE_AWAITING_CONFIRMATION, andpublication.confirmedturnstrueonly with evidence listed underpublication.evidence— the publisher’s notice, a verified value for that reference period, or the source calendar stating it happened.
Values, computed figures and revisions
- Decimal strings
- Every value is a decimal string exactly as published, with
unit.codeandunit.scalebeside it;nullis a period the publisher scheduled and has not filled, and is never zero. Parse with a decimal type. A float will change the last digit of a published figure and then disagree with the publisher. - What HELPERG computed, and how
- Where an official interface publishes an index and not the change the agency itself prints — the BLS statistical API supplies no calculations to an anonymous caller — the change is computed here in exact decimal arithmetic and the record carries
derived.formulaandderived.inputsso you can recompute it rather than trust it. A computed figure is HELPERG’s output and is labelled as such, never presented as the publisher’s own publication. - Revisions are vintages
- A revised value never overwrites the earlier one in storage: each is a vintage with its own sequence. What the public API serves is the current vintage of each reference period, with
revisionCount,isRevisionandpreviousValuedescribing the replacement; the earlier vintages themselves are not served, andperSerieson the releases endpoint widens to older periods, not to older vintages. A publication-status change alone — preliminary becoming final at the same number — is a revision too. - Not on this deployment
- A registered series whose values have not been ingested here answers 503 on the observations endpoint and is listed as unavailable by the releases endpoint rather than silently omitted. Which series those are is stated by the API, not assumed by the documentation.
Two recorded records
Both recorded from the live endpoints on 17 October 2026 at 15:02 UTC and trimmed to the fields discussed above. A calendar event whose scheduled time has passed without evidence of publication:
{
"id": "evt_2bbec48017745886b4a689f59f5def75",
"title": "Long term interest rates statistics",
"release": { "slug": "ecb-stats-irs-long-term-interest-rates", "publisher": "European Central Bank",
"officialPageUrl": "https://www.ecb.europa.eu/stats/…" },
"region": "EA", "currency": "EUR",
"referencePeriod": { "kind": "MONTH", "code": "2026-09", "startDate": "2026-09-01", "endDate": "2026-09-30" },
"scheduledDate": "2026-10-12", "scheduledAt": "2026-10-12T08:00:00.000Z",
"timePrecision": "EXACT_TIME", "sourceTimezone": "Europe/Berlin",
"status": "SCHEDULED", "derivedState": "PAST_DUE_AWAITING_CONFIRMATION",
"publication": { "releasedAt": null, "detectedAt": null, "confirmed": false, "evidence": [], "officialPublication": null },
"values": { "availability": "NO_SERIES_MAPPED", "actual": null, "previous": null },
"attribution": { "text": "Source: European Central Bank.", "required": true }
}A release whose value HELPERG computed from the publisher’s index, with the arithmetic and inputs it used:
{
"series": { "slug": "us-core-cpi-mom", "family": "core-consumer-price-index", "country": "US",
"frequency": "MONTHLY", "unit": { "code": "PERCENT", "scale": 0 },
"seasonalAdjustment": "SEASONALLY_ADJUSTED", "transformation": "MOM",
"officialSeriesUrl": "https://data.bls.gov/timeseries/CUSR0000SA0L1E" },
"referencePeriod": { "code": "2026-09" },
"value": "0.334848",
"publicationStatus": "NORMAL",
"publishedAt": null, "sourceUpdatedAt": null,
"ingestedAt": "2026-10-16T19:33:39.036Z",
"revision": { "count": 0, "isRevision": false, "previousValue": null },
"previous": { "referencePeriod": "2026-08", "value": "0.289796", "publicationStatus": "NORMAL" },
"derived": { "formula": "((CUSR0000SA0L1E[2026-09] / CUSR0000SA0L1E[2026-08]) - 1) * 100",
"inputs": [ { "sourceSeriesId": "CUSR0000SA0L1E", "referencePeriod": "2026-09", "value": "338.896" },
{ "sourceSeriesId": "CUSR0000SA0L1E", "referencePeriod": "2026-08", "value": "337.765" } ] },
"attribution": { "text": "Source: U.S. Bureau of Labor Statistics.", "required": true },
"links": { "observations": "/api/public/v1/economics/observations?series=us-core-cpi-mom", "calendar": "/api/public/v1/economics/calendar?release=bls-consumer-price-index" }
}Paging, errors, limits and browser access
- Paging
limitis clamped, never refused. When more exists,meta.nextCursoris an opaque token for the next page under the same filters;meta.truncated: truemeans a page was cut at the response ceiling and should be re-requested with a smallerlimit.- Errors
- One envelope,
{ "error": { "code", "message", "requestId" } }, with a stable code:VALIDATION_FAILED(400) for a malformed filter or window,NOT_FOUND(404) for an unknown series,RATE_LIMITED(429) withRetry-After, and 503 withRetry-Afterwhen the domain is unavailable — never an empty dataset in disguise. - Caching
- Responses carry
Cache-Control: public, max-ageat the cache seconds listed per endpoint, with a weakETagover the data section: send it back asIf-None-Matchand an unchanged answer costs a 304. - From a browser
- Not from a third-party web page:
Access-Control-Allow-Originis granted only to origins this deployment is configured with, never*, and every response carriesVary: Origin. Call the endpoints from your server.