Quick start
The API is JSON over HTTPS. Use the site's origin as the base URL (for example, https://ceme.ocarroeletrico.com). All data routes under /v1/ require authentication. For an app or backend integration, request an API key from the site operator and send it in the X-API-Key header. Do not put a private API key in browser code, a mobile app bundle, a public repository, or an LLM prompt.
curl 'https://ceme.ocarroeletrico.com/v1/cemes' \
-H 'X-API-Key: YOUR_API_KEY'
Responses are JSON. Successful list endpoints generally wrap results in {"data": [...]}. Errors use {"error":{"code":"...","message":"..."}}. Prices are in EUR; energy prices are EUR/kWh unless a field says otherwise.
Read-only: all documented endpoints only read or calculate data. POST /v1/simulate is still a calculation request; it does not change site data. Never call undocumented write routes.
Contents
Authentication
Recommended for integrations: an operator-provisioned API key. Send it as X-API-Key on every /v1/* request except session creation. Keys can be expired or revoked; a rejected key returns HTTP 401.
POST /v1/session issues an anonymous web-session JWT valid for 24 hours. It is intended for the website's browser, not as a substitute for an app API key. If used, send Authorization: Bearer TOKEN. Session creation is rate-limited and production requires the server's JWT secret to be configured.
curl -X POST 'https://ceme.ocarroeletrico.com/v1/session'
# Then use the returned token:
curl 'https://ceme.ocarroeletrico.com/v1/health' \
-H 'Authorization: Bearer TOKEN_FROM_RESPONSE'
Use one authentication method per request. Never log or commit credentials. HTTPS is required outside local development.
Read endpoints
| Method and path | Purpose / parameters |
GET /v1/health | Confirms authentication; reports credential kind and server time. |
GET /v1/cemes | Commercial energy suppliers (CEMEs). |
GET /v1/tariffs | Tariffs and the current tariff version where one is available. |
GET /v1/tar | Currently valid regulated network access tariffs (TAR). |
GET /v1/omie/daily?days=60 | Daily OMIE series, one row per Lisbon calendar day. days is clamped to 7–120; default 60. complete is false for a day that does not yet have all its quarter-hours (typically tomorrow, in the afternoon). |
GET /v1/omie/futures | OMIP futures data. |
GET /v1/omie/stats | Monthly OMIE statistics and reference values. |
GET /v1/indexed/quarters | Quarter-hour data and calculated prices for an indexed tariff. See below. |
POST /v1/simulate | Calculates and ranks tariffs for supplied usage inputs. See below. |
Exact field sets can evolve with the tariff data. Treat unknown JSON fields as valid, and do not assume every tariff has every optional value (for example, tariff version or split energy/network prices).
Calculate a comparison
POST /v1/simulate accepts a JSON object. All fields are optional; defaults are shown.
curl -X POST 'https://ceme.ocarroeletrico.com/v1/simulate' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data '{
"kwh": 40,
"region": "CONT",
"at": "2026-10-18T01:00:00Z",
"omie": 0.20
}'
| Field | Meaning and accepted values |
kwh | Energy to charge, number greater than 0 and at most 1000. Default 40. |
region | CONT (mainland), MAD (Madeira), or AZO (Azores). Default CONT. |
at | Optional valid date/time; ISO 8601 with timezone/UTC offset recommended. Defaults to now. |
omie | Reference OMIE price in EUR/kWh, positive number. Default 0.20. |
omieCycles | Optional per-cycle reference prices: {"DIARIO": {"vazio": …, "fora": …}, "SEMANAL": {"vazio": …, "fora": …}}, positive EUR/kWh. Each indexed tariff uses the pair for its own time-of-use cycle (daily or weekly). Recommended — take them from the last row of /v1/omie/daily with complete: true (bd_* = daily cycle, bs_* = weekly, divided by 1000). |
useSpot | Optional boolean. When true, indexed tariffs are priced at the OMIE of the quarter-hour containing at (and that quarter's loss profile), overriding the omie* references, which remain only as a fallback if that quarter is unavailable. The response then carries omieSpot ({utc, eurKwh}) and omieRef is that quarter's price. |
profile | Optional. The charge split over time, for a session that lasts long enough to cross the off-peak boundary, several OMIE quarter-hours, midnight or a DST change: [{"at": ISO, "kwh": n}, …], in time order, up to 400 slices, each kwh ≥ 0, total above 0 and up to 1000. Each slice is priced at its own instant: its off-peak/peak period, its quarter-hour OMIE (with useSpot), that quarter's losses and that day's TAR; activation, support and VAT apply once. Split at quarter-hour boundaries (:00, :15, :30, :45 UTC): a slice is priced entirely at its start. When present it wins: kwh is the sum and at the first slice. The response adds profile ({slices, from, to, withSpot}) and, per tariff, kwhByPeriod. Invalid input is 400 bad_profile. |
detail | Optional boolean, with profile. Each tariff then also carries slices: per slice the instant, kWh, period and final price excl. VAT, plus for indexed tariffs the OMIE used and whether it was the quarter's spot (omieSpot). |
omieVazio, omieFora | Optional positive EUR/kWh off-peak / peak reference prices applied to every split-period indexed tariff (an explicit override; ignored where omieCycles covers the tariff's cycle). |
The response includes at, kwh, region, omieRef, an average and data, an array of tariff quote objects sorted by total cost. A tariff's fields include descriptive metadata, prices and calculated totals; fields vary by tariff type. parts is the bill line by line, in euros (energyTar, plus energy/tar when the split is known, iec, activation, support, base excl. VAT, vatEur); the lines add up to totalEur to the cent. Some invalid/inconsistent tariffs can be omitted from the ranking rather than failing the whole request. This is an estimate for the energy component, not a final charging bill: the Mobi.E charging-point operator (OPC) fee is not included.
Indexed tariff quarter-hours
Use GET /v1/indexed/quarters with query parameters:
tariff: optional tariff ID. If omitted or not an indexed tariff, the server selects the first indexed tariff.
region: CONT, MAD, or AZO; defaults to CONT.
date: optional day in YYYY-MM-DD; defaults to today.
fixed: optional fixed tariff ID to compare against.
tz: optional IANA timezone for displayed start/end times, for example Europe/Lisbon; defaults to UTC.
curl 'https://ceme.ocarroeletrico.com/v1/indexed/quarters?tariff=INDEXED_ID®ion=CONT&date=2026-10-18&fixed=FIXED_ID&tz=Europe%2FLisbon' \
-H 'X-API-Key: YOUR_API_KEY'
The response includes quarter records, source/estimate metadata, and winner counts. With no valid fixed comparison, winner values are null. The response's UTC timestamp fields remain UTC; displayed clock labels use the requested timezone.
Errors, rate limits, and safe retries
| HTTP status | Meaning |
| 400 | Invalid request input; inspect error.code and error.message. |
| 401 | Missing, invalid, expired, or revoked credentials. |
| 405 | Wrong HTTP method; use the method listed above. |
| 413 | Request body exceeds the 64 KiB limit. |
| 429 | Rate limit reached. Wait at least the Retry-After seconds in the response. |
| 502 | Upstream OMIE series are temporarily unavailable; retry later with backoff. |
Responses include rate-limit headers such as X-RateLimit-Remaining and X-RateLimit-Reset. The default API allowance is 60 requests per minute per key (the configured per-client limit may differ). For retryable failures, use bounded exponential backoff and respect Retry-After; do not retry a 400 or 401 unchanged.
Guidance for AI agents and code generators
- Use only the documented read/calculation endpoints above. Do not infer that a route is safe just because its name looks familiar.
- Do not invent credentials or ask users to paste secrets into chat. Ask the integration owner to provision a key and provide it through the app's secret manager.
- Validate inputs and check the HTTP status before parsing a response as a successful result.
- Handle missing, null, and additional fields; tariff data is not a fixed catalogue schema.
- Keep units attached to values. In particular, OMIE inputs are EUR/kWh; do not pass EUR/MWh without converting.
- Describe the result as the energy portion of a charging estimate. Do not claim it includes the charging-point operator's fee or guarantees a final invoice.
- Do not expose an API key in client-side code or return it to an end user.
For implementation details and the canonical route/auth design, see the API and auth design notes.