Developers
The data behind the dashboards, for your own code.
Every register, series and computed figure the dashboards read is served from this domain as JSON, and over the Model Context Protocol for AI assistants. No key is needed. A free key adds a daily allowance of its own and a count of what it has used.
Overview
What the API serves.
The same rows the dashboards draw, with the publisher, unit and period on every series and the source on every computed figure.
- Official registers
- The Principal Buyer’s renewable project register, the SHARAKAT water portfolio and the Saudi Water Authority open-data catalogue, mirrored weekly, and each rebuilt for a past date from the change ledger.
- Mirrored series
- 30 series from the KAPSARC Energy Data Portal and the publishers each one names: load, capacity, tariffs, desalination, oil balance, prices and more, every point with its period, region and unit.
- Change ledger and schedule
- Every row-level change in a register or series with the value on both sides, and what is expected to refresh next.
- Model outputs
- The power balance against the 2030 target, the oil balance model under named scenarios, and the week ahead for the solar fleet. Modelled figures say so in the response.
- Publications
- The weekly brief, one permanent address per ISO week, and the report library with each report’s definitions and sources.
Without a key
With a free key
Every dataset, its source and its licence are listed in the data catalogue.
Authentication
A key is optional. Send it in one of two headers.
Create a key on your account page. It is shown once, when you create it: store it where your code reads secrets, and revoke it from the same page if it is exposed.
Either header
Authorization: Bearer rv_live_… X-API-Key: rv_live_…
A key that is sent must be valid
A request that carries a key is answered with 401 when the key is malformed, unknown or revoked, rather than quietly served without it. The body names the reason. Remove the header to fall back to the anonymous limit.
Keep keys on a server. A key in browser code is readable by anyone who opens the page.
Limits
Allowances by caller.
Limits keep the service available to every caller. Anonymous calls are limited per address over ten minutes; a key is limited per UTC day.
| Caller | Routes | Allowance | Counted by |
|---|---|---|---|
| Without a key | /api/status, /api/data/*, /api/brief*, /api/reports* | 300 requests per 10 minutes | Address, per server instance |
| Without a key | /api/search | 240 requests per 10 minutes | Address, per server instance |
| Without a key | /api/mcp | 300 requests per 10 minutes | Address, per server instance |
| Free key | All metered routes | 5,000 requests per UTC day | Key, across all instances |
| Licensed key | All metered routes | Set by the licence | Key, across all instances |
Every metered response says where you stand
- X-RateLimit-Limit
- The allowance in the current window.
- X-RateLimit-Remaining
- Requests left after this one.
- X-RateLimit-Reset
- When the window starts again, in epoch seconds. For a key, the next 00:00 UTC.
- Retry-After
- On a 429 only: seconds to wait.
Over the limit
The answer is 429 with a JSON body that names the limit: its scope, its tier, the allowance, the window and when it resets.
HTTP/1.1 429 Too Many Requests
{
"ok": false,
"error": "tooManyRequests",
"message": "This key has made its 5,000 requests for the day. The count starts again at 00:00 UTC (2026-09-25T00:00:00.000Z).",
"limit": {
"scope": "key",
"tier": "free",
"requests": 5000,
"window": "day",
"windowSeconds": 86400,
"resetAt": "2026-09-25T00:00:00.000Z"
},
"retryAfter": 3600,
"docs": "https://renewablevision.com.sa/en/developers#limits"
}Anonymous responses can be served from the edge cache, which neither counts them nor refreshes their headers. Keyed responses are marked private and always reach the origin, so every one is counted.
Endpoints
Every public route, from the OpenAPI document.
This reference is rendered from the same OpenAPI 3.1 document a code generator reads. Download it to generate a client, or import it into an API tool.
OpenAPI 3.1 document · https://renewablevision.com.sa/api/openapi.json
Data
| Endpoint | What it returns | Parameters | Response fields |
|---|---|---|---|
GET /api/statusMetered | Freshness of every mirrored series and register Every series and register with its publisher, period range, point count, last sync and status; every source the platform refreshes with its cadence, last successful refresh and state; and a self-check of every public endpoint, with the stored daily history where it exists. |
| checkedAt, sources, stale, degraded, series, registers, summary, feeds, endpoints, historySince, method Cite from: series[].publisher, registers[].publisher, checkedAt |
GET /api/data/portal/{series}Metered | One mirrored series: metadata and every point |
| series, points Cite from: series.publisher, series.source_url, series.licence, series.last_success_at |
GET /api/data/registers/{register}Metered | An official register, as last mirrored or as it stood on a past day |
| register, rows, vintage Cite from: register.publisher, register.source_url, register.last_success_at, vintage.asOf |
GET /api/data/projects/{id}Metered | One project from the Saudi Renewables Tracker, as its page shows it Capacity, technology, the stage this site counts with the Principal Buyer register beside it, developers with their published shares, the published tariff, modelled generation marked as modelled, and the project’s change-ledger entries. Location is locality and region only; plant coordinates are not published. |
| — Cite from: source, asOf, citation, note |
GET /api/data/companies/{company}Metered | One developer or shareholder and every tracked project that names it Published equity shares, capacity by stage and technology, capacity attributable by published equity share, and recent ledger changes across its projects. |
| — Cite from: source, asOf, citation, note |
GET /api/data/changesMetered | The change ledger: every row-level change, newest first Magnitudes carry their own unit and are not comparable across units, so ranking across them is left to the caller. |
| count, changes Cite from: changes[].sourceId, changes[].occurredOn |
GET /api/data/dueMetered | What is expected to refresh next, and what is late Refresh dates are this site’s own cadence, not a commitment by the publisher; commercial operation years are carried as the publisher states them. |
| asOf, windowDays, overdue, items, note Cite from: items[].publisher, asOf |
GET /api/data/power-balanceMetered | Operational capacity against the contracted pipeline and the 2030 target |
| month, operational, construction, tendered, trackedMw, target, system, reconciliation, stageOverrides, change, comparedWith, vintage, sources, method Cite from: sources[], method, month, vintage.computedAt |
GET /api/data/oil-modelMetered | The oil balance model run on the live inputs Modelled, not observed. Any lever can be set by query: demandGrowth, evShareEnd, powerDisplacement, petrochemicals, gasSwitching, opecPolicy, shaleElasticity, demandElasticity, inventorySensitivity. An unknown scenario falls back to base. |
| asOf, scenario, levers, sources, context, calibration, summary, months, method Cite from: sources[], method, asOf |
GET /api/data/fleet-outlookMetered | The week ahead for the tracked Saudi solar fleet Expected generation against the long-run normal, day by day, and the peak temperature forecast at the three main load centres. Aggregated across the fleet; no plant location is returned. | None | asOf, stale, days, plants, capacityMw, expectedGwh, normalGwh, deltaPct, daily, heat, sources, method Cite from: sources[], method, asOf |
Publications
| Endpoint | What it returns | Parameters | Response fields |
|---|---|---|---|
GET /api/briefMetered | Every weekly brief published, newest first |
| count, briefs Cite from: briefs[].url, briefs[].issued |
GET /api/brief/latestMetered | The current weekly brief |
| week, issued, locale, subject, preheader, blocks, social, summary, url, api Cite from: url, issued, week |
GET /api/brief/{week}Metered | One week’s brief at its permanent address |
| week, issued, locale, subject, preheader, blocks, social, summary, url, api Cite from: url, issued, week |
GET /api/reportsMetered | Every published report, newest edition first |
| count, reports Cite from: reports[].url, reports[].edition, reports[].contentUpdated |
GET /api/reports/{id}Metered | One report: caption, quoted definitions, sources and preview pages The figures are as filed on |
| report Cite from: report.url, report.sources, report.definitions, report.contentUpdated |
Search
| Endpoint | What it returns | Parameters | Response fields |
|---|---|---|---|
GET /api/searchMetered | Ranked matches across datasets, series, insights and projects |
| groups Cite from: groups[].items[].href |
MCP
| Endpoint | What it returns | Parameters | Response fields |
|---|---|---|---|
POST /api/mcpMetered | Model Context Protocol over Streamable HTTP JSON-RPC over POST, stateless, answered as JSON. Read-only tools over the catalogue, the registers, the trackers, the mirrored series, the change ledger, the schedule, the brief, the report library and the model outputs. Every tool result carries |
| — Cite from: result.structuredContent.source, result.structuredContent.asOf, result.structuredContent.citation |
GET /api/mcpNot metered | No server-sent-event stream Answers 405 with | None | — |
DELETE /api/mcpNot metered | End a session The endpoint keeps no session, so there is nothing to release. | None | — |
Cards and properties
| Endpoint | What it returns | Parameters | Response fields |
|---|---|---|---|
GET /api/cards/{card}Not metered | A 1200 × 630 share card with the current figures A PNG, cached for thirty minutes. Not metered: cards are embedded by pages and link previews. |
| — |
GET /api/propertiesNot metered | Thermodynamic properties from CoolProp’s reference equations of state A separate Python function, so it is not metered and takes no key. Reaction enthalpies are per mole of the named product, decomposed into formation and sensible terms, and checked against published values before they are displayed. Errors answer with a matching status and are never cached. |
| — |
Developer routes
| Endpoint | What it returns | Parameters | Response fields |
|---|---|---|---|
GET /api/openapi.jsonNot metered | The OpenAPI 3.1 document for every public route | None | — |
POST /api/licence-requestsNot metered | Request a data licence Filed for review; the owner is notified. Terms are agreed after the request has been read. Five requests per address per ten minutes. |
| message |
Site forms
Site forms serve this site’s own pages. They are listed so the document is complete; they take no key and are not part of the data API.
| Endpoint | What it returns | Parameters | Response fields |
|---|---|---|---|
POST /api/privacy-requestsNot metered | File a request under the Saudi Personal Data Protection Law Access, a copy, correction, deletion, withdrawal of consent, or something else. Stored and the owner notified; the answer goes to the email address given. Five requests per address per ten minutes. |
| message |
POST /api/inquiriesNot metered | The contact form | None | message |
POST /api/subscribeNot metered | Join the RV Plus and RV X materials list, with double opt-in | None | message |
GET /api/subscribe/confirmNot metered | The confirmation link: sends the visitor to the confirmation page | None | — |
POST /api/subscribe/confirmNot metered | Confirm a subscription from the confirmation page | None | — |
GET /api/subscribe/unsubscribeNot metered | The unsubscribe link: opens the page that confirms the removal | None | — |
POST /api/subscribe/unsubscribeNot metered | Leave the list, from the page or by one-click from a mail client | None | message |
POST /api/experts/applyNot metered | Apply to join the Expert Panel | None | message |
POST /api/experts/briefsNot metered | Ask the Expert Panel for a written opinion | None | message |
POST /api/askNot metered | RV Analyst: a question answered from this site’s data (members) Requires a signed-in member session. Streams newline-delimited JSON: text as it arrives, a line per data read, then the sources used. | None | — |
GET /api/materials/{id}Not metered | A member download: a report PDF or a data pack Requires a signed-in member session; answers with a short-lived signed URL. |
| — |
Code examples
One request, three ways.
Each fetches the latest weekly brief with a key and prints the rate-limit headers. Drop the key line to call anonymously.
curl
curl -i https://renewablevision.com.sa/api/brief/latest \ -H "Authorization: Bearer $RV_API_KEY"
Python
import os
import requests
res = requests.get(
"https://renewablevision.com.sa/api/brief/latest",
headers={"Authorization": f"Bearer {os.environ['RV_API_KEY']}"},
timeout=30,
)
res.raise_for_status()
brief = res.json()
print(brief["subject"], brief["url"])
print(res.headers["X-RateLimit-Remaining"], "requests left today")JavaScript
const res = await fetch('https://renewablevision.com.sa/api/brief/latest', {
headers: { Authorization: `Bearer ${process.env.RV_API_KEY}` }
});
if (res.status === 429) {
const { limit, retryAfter } = await res.json();
throw new Error(`Limit of ${limit.requests} per ${limit.window}; retry in ${retryAfter} s`);
}
const brief = await res.json();
console.log(brief.subject, brief.url);
console.log(res.headers.get('X-RateLimit-Remaining'), 'requests left today');MCP
Connect an AI assistant.
The same data is a Model Context Protocol server over Streamable HTTP. An assistant that accepts a remote MCP server needs only the URL. Every tool result carries the publisher, the date its figures are true as of, and the page to cite.
POST https://renewablevision.com.sa/api/mcp
Client configuration
{
"mcpServers": {
"renewable-vision": {
"type": "http",
"url": "https://renewablevision.com.sa/api/mcp",
"headers": {
"Authorization": "Bearer rv_live_…"
}
}
}
}To count an assistant’s calls against your key, add the key as an Authorization header in the client’s configuration. Without it the endpoint answers under the per-address limit.
Discovery record: /.well-known/mcp/server.json
Attribution
Cite the publisher and Renewable Vision.
Every figure has two sources to name: the body that published it, and this platform, which compiled, mirrored or computed it.
- Name the original publisher given in the response, and Renewable Vision, with the page the response links to.
- Give the date the figure is true as of, from the response, not the date you fetched it.
- Keep a modelled figure labelled as modelled. The response says which figures are.
- Third-party series keep their publishers’ licences. Where the upstream terms are non-commercial, so is any reuse.
- Renewable Vision compilations are CC BY-NC 4.0 unless a licence says otherwise.
Changelog
What changed, and when.
Routes are added without changing the ones already published. A change a caller could notice is listed here.
The same entries, with the rest of the platform’s changes, on the changelog
| Date | Change | Routes |
|---|---|---|
| 2026-09-24 | The status endpoint adds each source’s cadence, state and last error, a self-check of every public endpoint, and the stored check history where it exists. Privacy requests are filed from the site’s form. | /api/status, /api/privacy-requests |
| 2026-09-24 | Optional API keys with a daily allowance and a usage count; rate-limit headers on every metered response; the OpenAPI 3.1 document; data-licence requests. | /api/openapi.json, /api/licence-requests |
| 2026-09-24 | One tracker project and one company as JSON, with the fields to cite. | /api/data/projects/{id}, /api/data/companies/{company} |
| 2026-09-22 | The Model Context Protocol endpoint. | /api/mcp |
| 2026-09-22 | The report library as JSON. | /api/reports, /api/reports/{id} |
| 2026-09-21 | The weekly brief as JSON, one permanent address per ISO week. | /api/brief, /api/brief/latest, /api/brief/{week} |
| 2026-09-20 | The change ledger, the schedule of what is due, the power balance and the solar fleet outlook. | /api/data/changes, /api/data/due, /api/data/power-balance, /api/data/fleet-outlook |
| 2026-09-20 | `asOf`: a register as it stood at the end of a past day, and the power balance as stored for a past month. | /api/data/registers/{register}, /api/data/power-balance |
| 2026-09-14 | The oil balance model, with named scenarios and every lever settable by query. | /api/data/oil-model |
| 2026-09-13 | Data freshness, site search and share cards. | /api/status, /api/search, /api/cards/{card} |
| 2026-09-12 | Mirrored series from the KAPSARC Energy Data Portal, and the three official registers. | /api/data/portal/{series}, /api/data/registers/{register} |
Need to use the data commercially, or redistribute it?
Research, internal commercial use and redistribution are licensed separately, each with its own scope and attribution. A licence cannot override an upstream licence, and the licensing page says which datasets carry non-commercial upstream terms.