Overview
The forecast endpoint is asynchronous:
- POST
/forecast - upload your data and receive a job_id immediately.
- GET
/jobs/{job_id} - poll until status is completed or failed.
- 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
| Column | Type | Notes |
|---|
item_id | string | Series name, e.g. store_A, SKU-123 |
timestamp | string/datetime | ISO-8601 preferred: 2024-01-15 09:00:00 |
target | float | Observed 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"
| Parameter | Default | Description |
|---|
file | - | CSV or XLSX file (required) |
prediction_length | 24 | Number of future steps to forecast |
presets | chronos_tiny | Model size (see model list) |
item_id_column | item_id | Column name for series identifier |
timestamp_column | timestamp | Column name for datetime |
target_column | target | Column name for the metric |
freq | auto-detected | Pandas 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
| Status | Meaning |
|---|
pending | Job is queued |
running | Forecast is in progress |
completed | Forecast is ready to download |
failed | An 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
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')}")