Home / Docs

Electricarly API

A REST interface for Electricarly data: brands, drives, and full car specs.

Base URL: https://api.electricarly.info Format: JSON (Content-Type: application/json)


Authentication

Most endpoints require an API key passed via the X-API-Key header:

curl -H "X-API-Key: your-api-key-here" https://api.electricarly.info/api/brands
Endpoint Requires API Key
GET /health ❌ No
GET /api/brands/* ✅ Yes
GET /api/drives/* ✅ Yes
GET /api/specs/* ✅ Yes
GET /api/images/* ❌ No (signed URLs)
POST /admin/reload ✅ Yes

Set the API_KEYS environment variable to enable authentication (comma-separated list).

Requesting API access

If you'd like access to the authenticated API endpoints, please send a request to: louizo79@gmail.com. Include a short description of your intended use and any non-production hostnames or IPs that will access the API.


Rate Limiting

All API requests are rate limited per 1-minute window.

Scope Limit Notes
/api/brands, /api/drives, /api/specs Depends on your API key's tier (see below) Limited per X-API-Key, not per IP
/api/images 600 requests / minute Limited per IP (no API key required)
/admin/reload 5 requests / minute Requires API key
Everything else 300 requests / minute Limited per IP

Per-key limits

Each API key has its own independent quota — one key's usage never affects another's.

Tier Requests / minute
Standard 100 (default)
Custom / partner keys Varies — see your key's agreement, or ask when requesting access

Rate limit headers

Every response includes standard rate limit headers so you can track your usage:

Header Meaning
RateLimit-Limit Your limit for the current window
RateLimit-Remaining Requests remaining in the current window
RateLimit-Reset Seconds until the window resets

Exceeding the limit

If you exceed your limit, you'll receive a 429 Too Many Requests response:

{
  "error": "rate_limited",
  "message": "Too many requests, please try again later."
}

Wait until the window resets (see RateLimit-Reset) before retrying.


Health

GET /health

{
  "ok": true,
  "dataDir": "/path/to/output",
  "loadedAt": "2024-11-01T12:34:56.789Z",
  "counts": { "brands": 47, "drives": 47, "specs": 312 }
}

Image URL convention

Every image descriptor returned by the API has three fields:

Field Meaning Example
path On-disk path (POSIX) data/images/specs/afeela/899_Afeela_1_AWD/main.jpg
rel Relative to data/images/ specs/afeela/899_Afeela_1_AWD/main.jpg
url API path to fetch the file /api/images/specs/afeela/899_Afeela_1_AWD/main.jpg

Rule: always use url in clients. path and rel are for reference / server-side work.

If ABSOLUTE_IMAGES=1, every url is rewritten to http(s)://<host>/api/images/... at serialization time, so cross-origin clients can use them directly.


Brands

GET /api/brands

Query Type Default Description
q string — Substring match on name or slug
sort string name Sort field
order string asc asc or desc
page integer 1 Page number
limit integer 50 Items per page (max 200)
{
  "total": 47,
  "page": 1,
  "limit": 50,
  "items": [
    {
      "slug": "tesla",
      "brandId": "1",
      "name": "Tesla",
      "models": 17,
      "drivetrains": 76,
      "drivesCount": 76,
      "logoPath": "data/images/brands/tesla.png",
      "logoLocal": "brands/tesla.png",
      "logoUrl": "/api/images/brands/tesla.png"
    }
  ]
}

GET /api/brands/:slug

Returns the brand with its drives array inlined.

GET /api/brands/:slug/drives

Query Type Default Description
q string — Substring on display/model name
year string — Substring match on the year range
mainOnly boolean false Only main variants
page integer 1 Page number
limit integer 100 Items per page (max 500)

Each drive has:

{
  "driveId": "48",
  "slug": "tesla_model_x_100d_2017",
  "displayName": "Model X 100D",
  "year": "2017 - 2019",
  "isMain": true,
  "thumbPath": "data/images/specs/tesla/48_Tesla_Model_X_100D/thumb.jpg",
  "thumbLocal": "specs/tesla/48_Tesla_Model_X_100D/thumb.jpg",
  "thumbUrl": "/api/images/specs/tesla/48_Tesla_Model_X_100D/thumb.jpg",
  "url": { "slug": "tesla_model_x_100d_2017", "id": "48" }
}

GET /api/brands/:slug/specs

Paged list of specs for a brand.


Drives

GET /api/drives/:brandSlug/:driveId

Drive + spec summary.

{
  "drive": { "driveId": "48", "displayName": "Model X 100D", "thumbUrl": "/api/images/…" },
  "specSummary": {
    "fullModelName": "Tesla Model X 100D",
    "driveYear": "2017 - 2019",
    "spotlight": { "power-hl": "386", "range-hl": "523" },
    "thumbUrl": "/api/images/…"
  }
}

Specs

GET /api/specs/:brandSlug/:driveId

Full spec object with spotlight, specs, sections, trims, images, and localImages.

{
  "brandSlug": "afeela",
  "driveId": "899",
  "slug": "afeela_1_awd_2026",
  "fullModelName": "Afeela 1 AWD",
  "images": [
    {
      "path": "data/images/specs/afeela/899_Afeela_1_AWD/afeela-1-side-1.jpg",
      "rel":  "specs/afeela/899_Afeela_1_AWD/afeela-1-side-1.jpg",
      "url":  "/api/images/specs/afeela/899_Afeela_1_AWD/afeela-1-side-1.jpg"
    }
  ],
  "localImages": [
    "specs/afeela/899_Afeela_1_AWD/afeela-1-side-1.jpg"
  ]
}

GET /api/specs/by-slug/:slug

Keyed by the o slug.

GET /api/specs/search

Query Type Default Description
q string — Substring on full model name
brand string — Restrict to one brand slug
minRange number — Minimum WLTP range in km
minPower number — Minimum power in kW
page integer 1 Page number
limit integer 25 Items per page (max 100)

Each hit includes thumbUrl (first image) for quick rendering.


Images

GET /api/images/<rel-path>

<rel-path> is relative to data/images/:

GET /api/images/specs/afeela/899_Afeela_1_AWD/afeela-1-side-1.jpg
GET /api/images/brands/tesla.png

Returns the raw file with its content type. Path traversal is rejected (400) and missing files return 404.


Errors

All errors are JSON: { "error": "brand_not_found" }.

Status Meaning
400 Bad request (missing/traversal path)
404 Resource not found
429 Rate limit exceeded
500 Internal error