Developers

Timestral API reference

One endpoint turns a CSV into a probabilistic forecast. This page is the contract: authentication, request shape, response shape, errors, credits and rate limits. The OpenAPI file and a sample input/output are linked below.

Base URL

https://timestral.org

Authentication

Send an API key as a bearer token: Authorization: Bearer sk_.... Create a key at Settings → API keys after signing in with an email code.

Quickstart

The endpoint is synchronous. Send the CSV as a string, choose a horizon, and read the median plus the 80% band from data.rows. The full 21-quantile CSV is at data.fileUrl.

cURL
curl -sS https://timestral.org/api/forecast \
  -H "Authorization: Bearer $TIMESTRAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "csv": "date,sales\n2026-01-01,120\n2026-01-02,135\n2026-01-03,128\n2026-01-04,141\n2026-01-05,150\n2026-01-06,162\n2026-01-07,155\n2026-01-08,168\n",
    "steps": 6
  }'
Python
import os
import requests

csv_text = open("timestral-sample.csv", "r", encoding="utf-8").read()

response = requests.post(
    "https://timestral.org/api/forecast",
    headers={
        "Authorization": f"Bearer {os.environ['TIMESTRAL_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={"csv": csv_text, "steps": 12},
    timeout=120,
)
response.raise_for_status()
payload = response.json()

if payload["code"] != 0:
    raise RuntimeError(payload["message"])

result = payload["data"]
print(result["taskId"], result["took"])
for row in result["rows"][:5]:
    print(row["time"], row["median"], row["low"], row["high"])
print("quantile CSV:", result.get("fileUrl"))
JavaScript (fetch)
const apiKey = process.env.TIMESTRAL_API_KEY;

const response = await fetch("https://timestral.org/api/forecast", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    csv: await Bun.file("timestral-sample.csv").text(),
    steps: 12,
  }),
});

const payload = await response.json();
if (payload.code !== 0) throw new Error(payload.message);

const { taskId, rows, took, fileUrl } = payload.data;
console.log(taskId, took, rows.length, fileUrl);

POST /api/forecast

Forecast one or more numeric series. The first column may be a date or timestamp; if the first row is numeric, the file is read as values only. At least 8 rows, at most 8 numeric columns, and a maximum body size of 2 MB.

FieldTypeRequiredNotes
csvstringyesThe series as CSV text, up to 2 MB.
stepsintegernoHorizon, 1–1,024. Defaults to 12.

Response envelope:

200 OK
{
  "code": 0,
  "message": "ok",
  "data": {
    "taskId": "f0c2…",
    "columns": ["sales"],
    "rows": [
      {
        "time": "2026-01-09",
        "column": "sales",
        "low": 151.2,
        "median": 164.8,
        "high": 178.1
      }
    ],
    "took": "3.42 s",
    "fileUrl": "https://timestral.org/api/media/uploads/forecast-….csv"
  }
}

rows contains the median and the 80% band per time step. The full quantile table is in the downloadable CSV. A failed call returns a non-zero code and no data.

Errors and credits

CodeMeaningCredit
UnauthorizedMissing or invalid API keynot charged
invalid_csvEmpty, too large or unparseable CSVnot charged
too_few_rowsFewer than 8 rowsnot charged
no_numeric_columnNo numeric series foundnot charged
too_many_columnsMore than 8 numeric columnsnot charged
invalid_stepsHorizon outside 1–1,024not charged
insufficient_creditsNo credits leftnot charged
quota_exceededModel host is over quotarefunded
forecast_failedModel call failedrefunded

One credit is charged before the model call and refunded automatically if the call fails. A successful call costs one credit. There is no free API tier. Buy credits on the pricing page.

Rate limits and data handling

  • —At least 1.5 seconds between forecast requests per client. The API returns a 429-style error envelope when the limit is hit.
  • —The CSV is sent to the Timestral model host for inference. Result files are stored in Cloudflare R2 so the download link outlives the model host session.
  • —Uploads are not used to train a model. Do not send personal or regulated data you are not allowed to send to a third-party inference host.
  • —Each call creates a task row. Use taskId in support requests.

Other endpoints

EndpointAuthPurpose
GET /api/healthpublicLiveness and build SHA.
POST /api/forecast/emailpublic, rate-limitedEmail a browser-generated forecast CSV. Not a general email API.
GET /api/apikeyssession cookieList keys for the signed-in account.
POST /api/apikeyssession cookieCreate a key. Body: { title }.
DELETE /api/apikeys?id=session cookieSoft-delete a key.

What to read next