Skip to main content

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; modifiedByHelperg says 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. from and to bound an inclusive window — YYYY-MM-DD in tz, or ISO instants — defaulting to 7 days back and 31 ahead, at most 93 days. tz adds local.date, local.time and local.timeZone to each event while the UTC values stay. source, release, country and currency take up to 10 comma-separated tokens; region and status are closed enumerations and an unknown value is refused, not ignored. Pages of 50, at most 200, continued with meta.nextCursor. Shared cache: 300 seconds.
GET /api/public/v1/economics/releases
The newest published value of each registered series, merged newest first. series restricts to up to 10 slugs; country and region filter; perSeries returns 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), isRevision and previousValue (the value the current vintage replaced). Earlier vintages stay in storage and are not served by any public endpoint. series is required — an unknown slug is 404, a registered series with nothing ingested on this deployment is 503 — and from/to are reference-period codes such as 2026-08 or 2026-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/to bound the publication window, defaulting to the last 366 days; source, releaseFamily, series, country, topic and the closed state, linkStatus and type enumerations 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

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

JavaScript (fetch)
// 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

Python (standard library)
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) with scheduledDate, timePrecision and the sourceTimezone the publisher announced it in; a date-only announcement has local.time: null rather than an invented midnight. On a release or news item, publishedAt and sourceUpdatedAt are what the publisher states — null when it states nothing, never a guess.
HELPERG’s time
ingestedAt on a value and publication.detectedAt on 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.dataUpdatedAt means 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: there meta.dataUpdatedAt is the feed’s contentUpdatedAt, the newest change to a publication’s content, which a read that found nothing new leaves where it was. meta.generatedAt is merely when this response was built, and meta.cached with meta.cacheAgeSeconds says whether it came from HELPERG’s cache. meta.dataStatus is fresh, stale or unavailable, judged against yet another clock: on the calendar, the oldest successful source check among the calendar sources that have run — unavailable while 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 in meta.sourceChecks as sourceId, lastSuccessAt and latestCheck: successful, incomplete (the last run was partial or failed) or not_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 adds derivedState: when the scheduled time has passed with no evidence of publication the state is PAST_DUE_AWAITING_CONFIRMATION, and publication.confirmed turns true only with evidence listed under publication.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.code and unit.scale beside it; null is 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.formula and derived.inputs so 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, isRevision and previousValue describing the replacement; the earlier vintages themselves are not served, and perSeries on 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:

Calendar event, recorded and trimmed
{
  "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:

Release, recorded and trimmed
{
  "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
limit is clamped, never refused. When more exists, meta.nextCursor is an opaque token for the next page under the same filters; meta.truncated: true means a page was cut at the response ceiling and should be re-requested with a smaller limit.
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) with Retry-After, and 503 with Retry-After when the domain is unavailable — never an empty dataset in disguise.
Caching
Responses carry Cache-Control: public, max-age at the cache seconds listed per endpoint, with a weak ETag over the data section: send it back as If-None-Match and an unchanged answer costs a 304.
From a browser
Not from a third-party web page: Access-Control-Allow-Origin is granted only to origins this deployment is configured with, never *, and every response carries Vary: Origin. Call the endpoints from your server.

The pages these endpoints serve: the economic calendar, the released values and the official news; provider licences and attribution on the sources page; whether the economics domain is operational on API status. The weather endpoints have their own quickstart.