Credits are charged from what is actually returned, not from requested clock hours alone.
Credits = returned_valid_times * parameters * ceil(coordinates / 500)
returned_valid_times means the valid forecast times that the API actually returns per location. For ensemble queries, each member is its own time series.
Example (NBM-like sparse horizons): if your response returns about 100 valid times, with 1 parameter, for 3,500 coordinates, credits are:
100 * 1 * ceil(3500 / 500) = 700 credits
Cache hits are billed at 10% of normal credits.
You can verify usage and limits in your token dashboard.
Yes. If you need to backfill aggressively, we can configure temporary quota/throughput adjustments so you can consume more quota in a short period.
Please contact us first at info@gribstream.com so we can size and schedule the run without impacting shared capacity.
Include these details to speed up approval:
A 429 can happen for two common reasons:
Retry-After tells you when to retry:
Client best practice: honor Retry-After, apply exponential backoff with jitter, and avoid hot-loop retries.
This usually happens when a client keeps retrying denied requests at high rate.
401 or 429.Typical blocks are around 10 minutes to 1 hour, depending on traffic pattern and severity.
How to avoid it: stop retrying repeated 401 responses until auth is fixed, honor Retry-After on 429, and use exponential backoff with jitter plus retry caps.
Daily quotas reset at 00:00 UTC.
The exact countdown is shown in your token dashboard.
/timeseries and /runs?Both endpoints query the same model data, but they answer different questions.
/timeseries (alias: /history)asOf model-run cutoffs./runs (alias: /forecasts)forecastedFrom/forecastedUntil (not asOf)./timeseries: roughly valid_times * coordinates * members/runs: roughly runs * horizons * coordinates * membersIf you need one operational value per timestamp, use /timeseries. If you need every model cycle contribution, use /runs.
fromTime/untilTime or timesList?Use fromTime/untilTime for dense, continuous windows. Use timesList when you already know exact timestamps and need sparse extraction.
timesList: best for event timestamps, specific cycles, or sampled dates.In practice, timesList usually reduces over-fetching and credit usage because only the listed valid times are returned.
asOf do, and when should I use it?asOf is a model-run-time cutoff for /timeseries: only rows whose forecasted_at is at or before that timestamp are eligible.
Think of it as as of the model run timestamp, not as of the moment GribStream had indexed or exposed the data.
Use asOf when you need model-run-based backtesting, i.e. results that exclude later model cycles from the query.
If asOf is omitted, GribStream uses the latest available runs. /runs does not use asOf.
asOf reproduce the exact live API availability time?No. asOf uses the model run time, not the exact wall-clock time when a run first became available through GribStream.
For example, a 12Z GFS run can be eligible for asOf: "12:30Z" because its forecasted_at is 12:00Z, even if that run was not yet live in the API at 12:30Z.
If you need to approximate live API availability in a backtest, the standard public workflow is to apply a conservative availability buffer before setting asOf. Start from the historical decision time, subtract an estimate of the usual publication/indexing lag, and use that earlier timestamp as the asOf cutoff.
The buffer should be based on the usual lag between the nominal model cycle and when the provider publishes the relevant forecast horizons to public object/blob storage, with extra margin for occasional upstream delays and rare GribStream processing delays. For NOAA feeds such as GFS, files are uploaded in forecast-horizon order, so a workflow that only uses the first 48 forecast hours can often use a smaller correction than one that needs the full run.
GribStream does not yet publish per-dataset/per-horizon lag guidance in the model pages. We expect to handle that in a separate iteration. You can request includeMetadata: ["index_updated_at"] to see the latest ClickHouse index timestamp among the selected index rows used for each result row, but upstream corrections and occasional re-indexing mean it should not be treated as a stable "first available at" audit timestamp. Experimental index-time workflows may be available by request for customers who need this distinction, but they are not part of the stable public API contract.
forecasted_at or forecasted_time?Responses are streamed in a throughput-optimized order, so chronological ordering is not guaranteed.
If you need deterministic order, sort client-side after download:
/timeseries: sort by forecasted_time, then forecasted_at./runs: sort by forecasted_time, then run/horizon fields relevant to your workflow.name, level, info)?A variable selector is a JSON object like:
{ "name": "TMP", "level": "2 m above ground", "info": "" }
name: required parameter code (for example TMP, UGRD).level: required vertical/physical level.info: optional disambiguator; required when multiple fields share the same name + level.alias: optional output-column rename; does not change field selection.On each model page, the weather-variable dropdown/browser shows the exact JSON selector for each field and provides copy actions, so you can paste selectors directly into requests.
This usually means the requested grid does not intersect the dataset domain.
Common causes:
What to do:
For ensemble datasets, use the members array to choose which member forecasts to return.
members is omitted, GribStream returns the first available member only (typically control member 0)./api/v2/catalog/datasets/{dataset} and inspect members.For non-ensemble datasets, members is not used.
In /timeseries, each valid time uses the shortest eligible lead-time value under your filters. Around cycle boundaries, the selected source run can change, which may create step-like jumps.
Simple ways to reduce this:
/runs and keep a fixed run for the whole curve when you need run-consistent behavior.minLeadTime: "0h" and maxLeadTime: "0h".For large backfills, optimize for stable throughput and low overhead per returned point.
/timeseries and /runs), use timesList instead of broad range selectors.If you need temporary high-throughput backfills, contact us so we can plan a front-loaded window safely.
Cache hits are billed at 10% of normal credits and are usually much faster.
Cache tends to help most on data queried repeatedly, for example:
Important behavior:
> 10000 coordinates).Recommended baseline headers:
Authorization: Bearer <token>Content-Type: application/jsonAccept: text/csv or application/json or application/ndjsonAccept-Encoding: gzip (strongly recommended)For large responses, make sure your client accepts and decompresses gzipped payloads. This typically lowers bandwidth and improves end-to-end response time.
Use API expressions/filters when you can reduce data volume early:
Prefer post-processing when logic is heavier:
Practical default: filter/derive in the API first, then run advanced analytics downstream.
Yes. GribStream supports both workflows in the same API.
/timeseries with asOf to reconstruct the best forecast under a model-run-time cutoff.asOf.timesList when evaluation timestamps are sparse.This pattern works well for ML validation, risk backtests, and operational forecast-quality monitoring.
Both approaches are valid; they optimize for different tradeoffs.
Common strategy: use the API for product and analytics pipelines, and use raw archives for specialized research that needs full-file access.
Some weather models encode their gribfile weather parameters like wind in a vector form via it's components,
usually u and v.
You can convert the wind vector components u and v into a wind speed (magnitude)
and a wind direction (angle) using these formulas:
speed = math.sqrt(u*u + v*v)
direction = (270 - math.atan2(v, u) * 180 / math.pi) % 360
speed = math.sqrt(u*u + v*v)
U Component (u): Zonal (west → east).
V Component (v): Meridional (south → north).
Rotate the atan2 result to meteorological convention and wrap 0‑359°.
direction = (270 - math.atan2(v, u) * 180 / math.pi) % 360
curl -X POST 'https://gribstream.com/api/v2/hrrr/timeseries' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer [API_TOKEN]" \
-d '{
"fromTime": "2024-09-10T00:00:00Z",
"untilTime": "2024-09-10T10:00:00Z",
"minLeadTime": "1h",
"maxLeadTime": "50h",
"coordinates": [{ "lat": 40.7306, "lon": -73.9352, "name": "New York City" }],
"variables": [
{ "name": "UGRD", "level": "1000 mb", "alias": "uwind" },
{ "name": "VGRD", "level": "1000 mb", "alias": "vwind" }
],
"expressions": [
{ "expression": "func.Hypot(uwind, vwind)", "alias": "wind_magnitude" },
{ "expression": "int(270 - func.Atan2(vwind, uwind) * 180 / 3.14159) % 360", "alias": "wind_direction" }
]
}'
Result:
forecasted_at,forecasted_time,lat,lon,name,wind,uwind,wind_magnitude,wind_direction,vwind
2024-09-09T23:00:00Z,2024-09-10T00:00:00Z,40.731,-73.935,New York City,3.2632,6.1795,7.3362,237,3.9539
2024-09-10T03:00:00Z,2024-09-10T04:00:00Z,40.731,-73.935,New York City,2.2788,6.4841,6.9633,248,2.5385
2024-09-10T00:00:00Z,2024-09-10T01:00:00Z,40.731,-73.935,New York City,2.8097,8.0572,8.5765,249,2.9389
2024-09-10T02:00:00Z,2024-09-10T03:00:00Z,40.731,-73.935,New York City,2.4618,7.1213,7.3375,256,1.7680
2024-09-10T07:00:00Z,2024-09-10T08:00:00Z,40.731,-73.935,New York City,2.6267,6.5438,6.5864,263,0.7486
2024-09-10T05:00:00Z,2024-09-10T06:00:00Z,40.731,-73.935,New York City,2.3317,6.8368,6.8375,269,0.1001
2024-09-10T06:00:00Z,2024-09-10T07:00:00Z,40.731,-73.935,New York City,2.3682,7.3567,7.5201,258,1.5594
2024-09-10T04:00:00Z,2024-09-10T05:00:00Z,40.731,-73.935,New York City,2.6711,6.4087,7.1157,244,3.0921
2024-09-10T01:00:00Z,2024-09-10T02:00:00Z,40.731,-73.935,New York City,3.0152,8.1815,8.3435,258,1.6363
2024-09-10T08:00:00Z,2024-09-10T09:00:00Z,40.731,-73.935,New York City,2.1275,5.9424,5.9822,276,-0.6889
Use the Magnus‑Tetens approximation. Convert temperature from Kelvin to Celsius and apply the formula.
# T = temperature (K), RH = relative humidity (%)
T_C = T - 273.15
a = 17.27
b = 237.7
gamma = (a * T_C) / (b + T_C) + math.log(RH / 100)
dew_point_C = (b * gamma) / (a - gamma)
Compute dew point directly in the API response:
curl -X POST 'https://gribstream.com/api/v2/gfs/timeseries' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer [API_TOKEN]" \
-d '{
"fromTime": "2024-12-01T00:00:00Z",
"untilTime": "2024-12-01T06:00:00Z",
"minLeadTime": "0h",
"maxLeadTime": "12h",
"coordinates": [{ "lat": 47.6, "lon": -122.33, "name": "Seattle" }],
"variables": [
{ "name": "TMP", "level": "2 m above ground", "alias": "tempK" },
{ "name": "RH", "level": "2 m above ground", "alias": "rh" }
],
"expressions": [
{ "expression": "tempK - 273.15", "alias": "tempC" },
{ "expression": "(17.27 * tempC) / (237.7 + tempC) + func.Log(rh / 100)", "alias": "gamma" },
{ "expression": "(237.7 * gamma) / (17.27 - gamma)", "alias": "dew_point_C" },
{ "expression": "dew_point_C + 273.15", "alias": "dew_point_K" }
]
}'
Result:
forecasted_at,forecasted_time,lat,lon,name,rh,tempC,gamma,dew_point_C,dew_point_K,tempK
2024-12-01T00:00:00Z,2024-12-01T02:00:00Z,47.600,-122.330,Seattle,75.4000,4.9410,0.0693,0.9579,274.1079,278.0910
2024-12-01T00:00:00Z,2024-12-01T00:00:00Z,47.600,-122.330,Seattle,69.7000,5.7435,0.0465,0.6415,273.7915,278.8935
2024-12-01T00:00:00Z,2024-12-01T04:00:00Z,47.600,-122.330,Seattle,80.0000,4.4245,0.0924,1.2792,274.4292,277.5745
2024-12-01T00:00:00Z,2024-12-01T03:00:00Z,47.600,-122.330,Seattle,77.9000,4.6903,0.0844,1.1678,274.3178,277.8403
2024-12-01T00:00:00Z,2024-12-01T01:00:00Z,47.600,-122.330,Seattle,73.6000,5.0681,0.0540,0.7457,273.8957,278.2181
2024-12-01T00:00:00Z,2024-12-01T05:00:00Z,47.600,-122.330,Seattle,82.3000,4.0860,0.0971,1.3433,274.4933,277.2360