Skip to main content

Overview

The forecast endpoint is asynchronous:
  1. POST /forecast - upload your data and receive a job_id immediately.
  2. GET /jobs/{job_id} - poll until status is completed or failed.
  3. GET /jobs/{job_id}/download - download the forecast CSV.
This design lets the API handle long-running forecasts (minutes for large datasets) without blocking the HTTP connection.

Step 1 - Prepare your data

Required columns

ColumnTypeNotes
item_idstringSeries name, e.g. store_A, SKU-123
timestampstring/datetimeISO-8601 preferred: 2024-01-15 09:00:00
targetfloatObserved value at each timestamp

Example CSV

item_id,timestamp,target
store_A,2024-01-01,1500.0
store_A,2024-01-02,1620.0
store_A,2024-01-03,1480.0
store_B,2024-01-01,890.0
store_B,2024-01-02,910.0
store_B,2024-01-03,875.0
Each series must have at least 2 historical observations. More data improves accuracy (10-100 points per series is a good target).

Step 2 - Submit the job

curl -X POST https://eomer-api.onrender.com/forecast \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@data.csv" \
  -F "prediction_length=7" \
  -F "presets=chronos_small" \
  -F "item_id_column=item_id" \
  -F "timestamp_column=timestamp" \
  -F "target_column=target"

Form parameters

ParameterDefaultDescription
file-CSV or XLSX file (required)
prediction_length24Number of future steps to forecast
presetschronos_tinyModel size (see model list)
item_id_columnitem_idColumn name for series identifier
timestamp_columntimestampColumn name for datetime
target_columntargetColumn name for the metric
freqauto-detectedPandas frequency string, e.g. D, H, W

Response (202 Accepted)

{
  "job_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "pending",
  "created_at": "2024-01-15T09:00:00+00:00",
  "prediction_length": 7
}

Step 3 - Poll for completion

curl https://eomer-api.onrender.com/jobs/3fa85f64-5717-4562-b3fc-2c963f66afa6 \
  -H "Authorization: Bearer YOUR_API_KEY"

Status values

StatusMeaning
pendingJob is queued
runningForecast is in progress
completedForecast is ready to download
failedAn error occurred - check the error field

Completed response

{
  "job_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "completed",
  "created_at": "2024-01-15T09:00:00+00:00",
  "completed_at": "2024-01-15T09:02:15+00:00",
  "elapsed_seconds": 135.4,
  "num_items": 2,
  "prediction_length": 7,
  "download_url": "/jobs/3fa85f64-5717-4562-b3fc-2c963f66afa6/download"
}

Step 4 - Download the forecast

curl https://eomer-api.onrender.com/jobs/3fa85f64-5717-4562-b3fc-2c963f66afa6/download \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -o forecast.csv

Output format

item_id,timestamp,mean,0.1,0.5,0.9
store_A,2024-01-08,1560.2,1420.1,1548.9,1698.3
store_A,2024-01-09,1575.8,1433.5,1564.2,1715.7
store_B,2024-01-08,895.1,822.4,892.6,967.8
  • mean - point forecast
  • 0.1 / 0.5 / 0.9 - 10th, 50th (median), 90th quantile forecasts

Cleanup

Jobs and output files are automatically deleted after 1 hour (configurable via EOMER_JOB_TTL_SECONDS). To delete a job early:
curl -X DELETE \
  https://eomer-api.onrender.com/jobs/3fa85f64-5717-4562-b3fc-2c963f66afa6 \
  -H "Authorization: Bearer YOUR_API_KEY"
Returns 204 No Content on success.

Python example (end-to-end)

import time
import httpx

API_BASE = "https://eomer-api.onrender.com"
API_KEY  = "your-api-key-here"
HEADERS  = {"Authorization": f"Bearer {API_KEY}"}

# 1. Submit
with open("data.csv", "rb") as f:
    resp = httpx.post(
        f"{API_BASE}/forecast",
        headers=HEADERS,
        files={"file": ("data.csv", f, "text/csv")},
        data={"prediction_length": "7", "presets": "chronos_small"},
    )
resp.raise_for_status()
job_id = resp.json()["job_id"]
print(f"Submitted job: {job_id}")

# 2. Poll
while True:
    resp = httpx.get(f"{API_BASE}/jobs/{job_id}", headers=HEADERS)
    resp.raise_for_status()
    body = resp.json()
    print(f"Status: {body['status']}")
    if body["status"] in ("completed", "failed"):
        break
    time.sleep(5)

# 3. Download
if body["status"] == "completed":
    resp = httpx.get(f"{API_BASE}/jobs/{job_id}/download", headers=HEADERS)
    resp.raise_for_status()
    with open("forecast.csv", "wb") as f:
        f.write(resp.content)
    print("Forecast saved to forecast.csv")
else:
    print(f"Job failed: {body.get('error')}")