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. Most routes need no key; the Energy Atlas’s layers are free with membership and need a member’s key. A free key adds a daily allowance of its own and a count of what it has used.
API console
Call any endpoint, live, and see what comes back.
Pick an operation from the OpenAPI document, fill its parameters and send it to this site’s API. The answer shows as a tree, a table and, where the rows allow, a chart, with the rate-limit headers it came back with and the same call in four languages.
One project from the Saudi Renewables Tracker, as its page shows it
Parameters
A tracker id, e.g. sudair-pv-1500-mw, or a captive segment id, e.g. nadec-haradh-pv.
Response
Choose an operation and send it. The answer appears here as a tree, a table and, where the rows allow, a chart.
Code
curl -sS 'https://renewablevision.com.sa/api/data/projects/sudair-pv-1500-mw'
Requests go to this site only. Without a key they count against your address’s ten-minute allowance; with one, against the key’s daily allowance, as the headers show.
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
- 33 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 for most routes. 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_…
The Energy Atlas’s layers need a member key
The atlas’s project layer (/api/data/atlas), the resource grid (/api/data/resource/grid), the GCC country series (/api/data/gcc-series), the Saudi tracker file (/data/saudi-renewables.json), the modelled output of every tracked project (/api/data/project-output, and the PVGIS rows at /api/data/integrations/pvgis), the bulk files that carry them (gcc-renewables, saudi-captive-solar, resource-grid, gcc-series and project-output) and the MCP tools that read them are free with membership. Send a member’s key, or call them from the site while signed in. Without either, the answer is 401 membersOnly, with the reason in English and Arabic and a link to join.
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, on every route but the members-only ones.
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
Integrations · Every upstream source, its licence, cadence and health.
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. A project in the tracker’s captive and corporate segment answers at the same address with its own record: capacity as its source states it, owners, developer and offtaker, the contract type, every source with the date it was read, and the readings where sources disagree. 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/gcc-projectsMetered | The GCC Renewables Tracker: utility-scale projects in five GCC states Solar PV, concentrated solar, wind and battery projects of 50 MW or more in the UAE, Oman, Qatar, Kuwait and Bahrain, compiled from the procurers and developers. Each row carries its sources with the date they were read, the stage in the source’s own words, and the readings where sources disagree. Capacity is as each source states it (MWac, MWp or MW); a tariff appears only where one was published. Location is locality and region only. |
| — Cite from: source, asOf, citation, note, projects[].sources |
GET /api/data/gcc-projects/{id}Metered | One project from the GCC Renewables Tracker, as its page shows it Capacity as the source states it, owners with the shares they publish, procurer and offtaker, the stage in the source’s own words, the published tariff where there is one, every source with the date it was read, and the readings where sources disagree. Plant coordinates are not published. |
| — Cite from: source, asOf, citation, note, project.sources |
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, nextBefore, 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 |
GET /api/data/resourceMetered | The solar and wind resource for one 0.5° cell of the NASA POWER grid Long-run means (NASA POWER climatology, 2001–2020) of global horizontal and direct normal irradiance, wind speed at 50 m, air temperature and aerosol optical depth at 550 nm (the dust that soils panels), annual and by month, for the 0.5° cell a place falls in. Ask with exactly one of |
| — Cite from: source, asOf, citation, note, licence, acknowledgement, period |
GET /api/data/resource/gridMetered | The whole NASA POWER resource grid behind the Energy Atlas (members) Five parameters (global horizontal and direct normal irradiance, wind at 50 m, air temperature, aerosol optical depth), the annual mean and every month, on 0.5° nodes from 12°N to 33°N and 34°E to 60°E. Each layer is quantised and delta-encoded along each row; | None | — Cite from: source, asOf, citation, note, meta.licence, meta.acknowledgement |
GET /api/data/gcc-seriesMetered | Country series for the six GCC states: capacity, generation, demand, emissions (members) IRENA installed solar, wind and renewable capacity; Our World in Data solar and wind generation, shares of electricity, demand and the carbon intensity of electricity; World Bank CO2 per person, population and renewable share of final energy. By country and year, each metric with its own publisher, licence and unit. Mirrored weekly, with a committed snapshot behind it. Members only: send a member’s API key (keys are issued to free members at /account/api), or call it from this site while signed in. Without either the answer is 401 |
| — Cite from: source, asOf, citation, note, metrics[].source |
GET /api/data/newsMetered | Headlines on GCC energy, from publishers’ feeds and GDELT Gathered every three hours from publishers’ own RSS and Atom feeds and, best effort, the GDELT Project’s DOC 2.0 API; only headlines about GCC energy (renewables, storage, hydrogen, power, oil and gas, water, data-centre power) are kept. Each item is a headline, its link, the publisher’s domain, the date and its language; no summary or article text is stored. Items carry the tracker projects and companies the headline names unambiguously. Empty until the news table exists. |
| — Cite from: source, asOf, citation, note, headlines[].url, headlines[].domain |
GET /api/data/atlasMetered | The Energy Atlas project layer, placed at locality level (members) Every tracked Saudi, GCC and captive project with stage, capacity as stated, commercial operation year (actual or expected), published tariff and developers, each placed at a public town centroid rounded to 0.1° or at its region or country ( |
| projects, undated, storage, dataCentres, sources Cite from: source, asOf, citation, note, sources, storage[].reports, storage[].sources |
GET /api/data/projectsMetered | Every project from every tracker, filtered by any facet (members) The project index: the Saudi Renewables Tracker, its captive and corporate segment, the GCC Renewables Tracker, Saudi battery storage, Saudi data centres, the global CCUS tracker and the IEA hydrogen production projects database, each row described on the same facets (place, sector, technology, stage, commitment, milestones, investment, capacity, actors with roles, programme, source and verification). The same query language as the page |
| — Cite from: source, asOf, citation, note, projects[].provenance |
GET /api/data/largestMetered | The largest Saudi solar, wind and hybrid projects as an Excel workbook (members) The lists on /data/renewables/saudi-arabia/largest as an .xlsx: one sheet per kind (solar, wind, hybrid), largest first, with the capacity in MW, technology, stage, operation year (target or actual), region, developers and each project’s page, and a sheet with the source, the as-of date and the licence line. One of a member’s monthly downloads; unlimited with RV Plus. Members only: send a member’s API key (keys are issued to free members at /account/api), or call it from this site while signed in. Without either the answer is 401 |
| — Cite from: Source sheet |
GET /api/tenders/xlsxMetered | The tenders board as an Excel workbook (members) The energy and water tenders on /tenders as an .xlsx, newest first: what is tendered, the buyer, the market, the sector, the stage, the contract model, the size, the bid deadline when announced, the announcement date and who it suits; and a sheet with the as-of date and the licence line. Facts only, never the headline or its publisher. One of a member’s monthly downloads; unlimited with RV Plus. Members only: send a member’s API key (keys are issued to free members at /account/api), or call it from this site while signed in. Without either the answer is 401 |
| — Cite from: About sheet |
GET /api/data/integrationsMetered | Every upstream source the site reads, with its licence and health Publisher, what is taken, the licence and how far it lets rows travel, refresh cadence, the last successful pull, rows held, the pull history where it exists, and a state: live, awaiting key, awaiting access, failed or pending. The page at /developers/integrations renders the same list. | None | — Cite from: source, asOf, citation |
GET /api/data/integrations/{source}Metered | One integrated source’s rows, filtered, with its upstream licence Filters depend on the source (country, year, sector, indicator, product, partner, topic, city, company); an unknown filter or a value the rows do not hold is refused with the list of what exists. CSV where the licence allows; UN Comtrade and PeeringDB answer 403 |
| — Cite from: source, asOf, citation, note, licence |
GET /api/data/project-outputMetered | Estimated annual output of every tracked solar project, modelled with PVGIS (members) Annual GWh and capacity factor for each solar project in the Saudi tracker, the GCC tracker and the captive segment, fixed at the optimal tilt and on a single-axis tracker, from PVGIS yields at the town or region centroid (never the plant), with totals by tracker and stage. Modelled, not measured. Members only: send a member’s API key (keys are issued to free members at /account/api), or call it from this site while signed in. Without either the answer is 401 |
| — Cite from: source, asOf, citation, note, licence |
GET /api/data/project-output/{id}Metered | One project’s estimated annual output, with its tracker’s totals by stage |
| — Cite from: source, asOf, citation, note, licence |
GET /api/data/tradeMetered | Clean-energy equipment imports into the GCC by product, year and partner PV cells and modules, lithium-ion cells, wind turbines, static converters, electrolysis machines and large transformers, 2015 onward, by product group, year and the five largest partners. Derived from UN Comtrade: aggregates only, and no CSV, because the upstream terms do not allow the rows to be republished as published. |
| — Cite from: source, asOf, citation, note, licence |
GET /api/data/equipment-flowsMetered | Clean-energy equipment flows into the GCC by importer, exporter, product and year PV cells and modules, lithium-ion cells, liquid-filled transformers, static converters, wind turbines and electrolysis machines imported by each GCC state from every exporter, 2017 onward, from CEPII BACI (reconciled from UN Comtrade, FOB, current US dollars). Etalab Open Licence 2.0: CSV is offered with the source named. |
| — Cite from: source, asOf, citation, note, licence |
GET /api/data/emissionsMetered | GCC greenhouse gas emissions by source, country, sector and year Climate TRACE by sector, the Climate Watch historical series, and Our World in Data per-person and intensity figures, each row naming its source. National aggregates only; the methods differ and are never summed. |
| — Cite from: source, asOf, citation, note, licence |
GET /api/data/oil-strategy/outlookMetered | The oil price outlook: model path, fan and benchmarks Brent today; the model’s monthly path for 24 months under current policy, anchored to the EIA outlook blended with the futures strip (with the unanchored structural path beside it), with a fixed, seeded Monte Carlo fan; the balance and OECD stocks behind it; the EIA Short-Term Energy Outlook and the futures strip beside it; World Bank and IMF benchmarks; the inventory–price calibration with its fit statistics; and what changed since the previous outlook. Modelled scenario analysis, not a forecast. | None | — Cite from: source, asOf, citation, note |
GET /api/data/oil-strategy/presetsMetered | Five production paths compared Hold current policy, gradual and accelerated increases, a deeper cut and a market-share push, each with its monthly and annual paths and a summary: price, Saudi exports, oil revenue, fiscal balance against the IMF breakeven, market share, spare capacity, net present value and the US shale response. |
| — Cite from: source, asOf, citation, note |
GET /api/data/oil-strategy/scenarioMetered | One preset production path, in full One preset under any shocks, with every monthly and annual variable and its difference from current policy. CSV for members. |
| — Cite from: source, asOf, citation, note |
POST /api/data/oil-strategy/scenarioMetered | Run a production scenario, with Monte Carlo and the frontier A preset or a custom path, any parameter, any shocks, and optionally a Monte Carlo run and the frontier search. Validated strictly and bounded; counts as five requests ( |
| — Cite from: source, asOf, citation, note |
GET /api/data/oil-strategy/backtestMetered | The near-term model scored against history The near-term model run from mid-2014, early 2017, early 2020, early 2022 and early 2024 on the EIA outlook of the time, against the Brent that followed: the anchored path (the default), the unanchored structural model, no change and the outlook itself. Mean absolute errors by horizon, every method on the same months. | None | — Cite from: source, asOf, citation, note |
GET /api/data/bulkMetered | Every dataset, and whether it can be downloaded whole Each dataset with its licence and, where a file is allowed, the size, row count, as-of date and ETag of its CSV and JSON. Datasets whose upstream terms do not allow redistribution say why. 12 of 35 are available. |
| weight, available, datasets Cite from: source, asOf, citation, datasets[].licence, datasets[].asOf |
GET /api/data/bulk/{file}Metered | A whole dataset as one CSV or JSON file, with its licence manifest The rows of one dataset and a manifest naming the publisher and licence behind each column group (in the JSON; the CSV's first line links to it). Strong ETag: send If-None-Match to get 304. A full file counts as 10 requests, a 304 as one. Refused with 403 and the reason where the upstream licence forbids redistribution. No row carries a plant location. |
| dataset, citation, manifest, excluded, count, rows Cite from: dataset.licence, dataset.asOf, manifest[].licence, manifest[].attribution, citation |
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 |
GET /api/postman.jsonNot metered | This API as a Postman collection, generated from the OpenAPI document Postman Collection v2.1, one folder per tag and one request per operation, with example values. Set the | None | — |
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 The only place an enquiry is written. A | None | message |
POST /api/coverage-requestsNot metered | Ask for coverage the site does not have yet Two kinds of request. |
| message |
POST /api/coverage-requests/{id}/voteNot metered | Vote for a request on the data-requests board A signed-in member’s vote: one per member per request, taken back by sending it again, or set outright with |
| voted, votes |
POST /api/coverage-requests/{id}/briefNot metered | Ask for a request on the board as a custom brief A signed-in member asks for a request to be answered for them. It reaches the company’s inbox as one enquiry, linked to the request and carrying the organisation of the member’s verified work email; the member is told a proposal will follow. No price is quoted. Asking again for the same request returns the first enquiry. Five per member an hour. |
| already |
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 |
GET /api/subscribe/weeklyNot metered | Whether the signed-in member already receives the weekly update | None | message |
POST /api/subscribe/weeklyNot metered | The one-field weekly update: double opt-in for an email address, or a member's one click on their own address | None | message |
POST /api/experts/readNot metered | Read a signed-in member's CV (a PDF) into the application's fields | None | message |
POST /api/experts/applyNot metered | Apply as a specialist (a signed-in member) | None | message |
POST /api/experts/briefsNot metered | Send a question to Renewable Vision's analysts | 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/assistantNot metered | Whether the site assistant can answer now (docs/ASSISTANT.md) | None | ready |
POST /api/assistantNot metered | The site assistant: a visitor’s question answered from the site’s pages and data For the site’s own widget. Streams newline-delimited JSON: text as it is written, the cards the answer shows (pages, membership, an enquiry form, offers), then | None | — |
GET /api/discoverNot metered | The Discover deck: cards for the site’s trackers, maps and projects, with live figures For the site’s own Discover deck (docs/ASSISTANT.md). Each card carries one figure from the loader its page reads and the date that figure is true as of; quick guesses carry three choices, one of them the real figure. The same for every reader; no cookie is read. Nothing paid is ever a card. |
| — |
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');Client libraries
TypeScript and Python clients, generated from the OpenAPI document.
One typed method per operation, the same names in both languages, and the behaviour a careful client needs: waiting out a 429 before retrying, following the change ledger page by page, revalidating bulk files with their ETag, and checking webhook signatures.
TypeScript
No dependencies: the platform’s fetch, in Node 18 and later, Deno, Bun and the browser. Types for every operation’s parameters.
Package: @renewable-vision/sdk · Source: sdk/typescript
import { RenewableVision, verifyWebhook } from '@renewable-vision/sdk';
const rv = new RenewableVision({ apiKey: process.env.RV_API_KEY });
// Typed calls, one per operation, with rate-limit-aware retry built in.
const { data, rateLimit } = await rv.getProject({ id: 'sudair-pv-1500-mw' });
console.log(data.project, rateLimit.remaining);
// Every page of the change ledger since a date.
for await (const change of rv.paginateChanges({ since: '2026-09-01' })) {
console.log(change.entityName, change.change);
}
// A whole dataset, revalidated with its ETag.
const file = await rv.downloadBulk('gcc-renewables', 'csv');Python
Built on requests, typed with TypedDict and dataclasses, for Python 3.9 and later.
Package: renewable-vision · Source: sdk/python
import os
from renewable_vision import RenewableVision, verify_webhook
rv = RenewableVision(api_key=os.environ.get("RV_API_KEY"))
res = rv.get_project(id="sudair-pv-1500-mw")
print(res.data["project"], res.rate_limit.remaining)
for change in rv.paginate_changes(since="2026-09-01"):
print(change["entityName"], change["change"])
file = rv.download_bulk("gcc-renewables", "csv")- A 429 is retried after the Retry-After the API sends, up to three times; a 5xx is retried with a short backoff.
- The change ledger is read page by page with a date cursor, so a long history needs no bookkeeping.
- verifyWebhook and verify_webhook check the RV-Signature header, the timestamp and every v1 value in constant time.
- The first release to npm and PyPI is pending; the calls read as shown here.
Webhooks
A signed POST to your endpoint when something changes.
Key holders can subscribe from the API keys page to changes in projects, datasets, headlines and the weekly brief, optionally narrowed to named projects, companies or datasets. Each delivery is the event as JSON, signed with the subscription’s own secret.
| Event | Sent when |
|---|---|
project.stage_changed | A project’s stage changed in a register or tracker. |
project.added | A project was added to a register or tracker. |
project.updated | A project’s capacity, date or listing changed. |
dataset.refreshed | A register, series, tracker or source was refreshed from its publisher. |
news.mention | A headline named a tracked project or company. |
brief.published | The weekly brief was published. |
Signing and replay protection
The RV-Signature header carries t, the Unix time of signing, and v1, the HMAC-SHA256 of the timestamp, a full stop and the raw body, keyed with your secret. Recompute it over the exact bytes you received and compare in constant time. Refuse a timestamp more than 300 seconds from your clock, and keep the event ids you have handled, since a retry sends the same id.
RV-Signature: t=1790236800,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
Delivery and retries
Answer with any 2xx within ten seconds. Anything else is retried after 1 min, 5 min, 30 min, 2 h, 12 h: 6 attempts in all. When the last one fails the subscription is disabled and the key’s owner is told by email. A 410 Gone disables it at once. The log on the API keys page shows every attempt: the status code, the latency and the error class, never your response body.
After a secret is rotated, the old secret keeps signing beside the new one for a day, so the header carries two v1 values; accept either while you switch.
Endpoints must be https on a public address. Private, loopback and link-local addresses are refused when the subscription is saved and again when each delivery connects, and redirects are not followed.
Verify with the TypeScript client
import { verifyWebhook } from '@renewable-vision/sdk';
// Express, with the raw body kept: the signature is over the exact bytes.
app.post('/hooks/rv', express.raw({ type: 'application/json' }), async (req, res) => {
const ok = await verifyWebhook({
payload: req.body,
header: req.get('RV-Signature'),
secret: process.env.RV_WEBHOOK_SECRET
});
if (!ok) return res.sendStatus(400);
const event = JSON.parse(req.body);
if (alreadySeen(event.id)) return res.sendStatus(200); // retries reuse the id
queue(event);
res.sendStatus(204);
});Verify with the Python standard library
import hmac, hashlib, time
def verify(header: str, body: bytes, secret: str, tolerance: int = 300) -> bool:
parts = [p.split("=", 1) for p in header.split(",") if "=" in p]
t = next((int(v) for k, v in parts if k == "t"), None)
sigs = [v for k, v in parts if k == "v1"]
if t is None or abs(time.time() - t) > tolerance:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
return any(hmac.compare_digest(expected, s) for s in sigs)Bulk downloads
Whole datasets as CSV and JSON, where their licences allow.
12 of 35 datasets can be downloaded whole. Each file carries a licence manifest naming the publisher and licence behind each group of columns, a strong ETag for revalidation, and counts as 10 requests; a 304 counts as one.
curl -sS -D - -o global-ccus.csv https://renewablevision.com.sa/api/data/bulk/global-ccus.csv # later: ask again with the ETag; 304 costs one request, not 10 curl -sS -D - -H 'If-None-Match: "<etag>"' https://renewablevision.com.sa/api/data/bulk/global-ccus.csv
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, and the tools that read the Energy Atlas’s layers (search_projects over the Saudi, captive, GCC and data-centre trackers, gcc_series, and project_output_estimate without a project) refuse, because they are free with membership.
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-10-01 | Members only from this release: `/api/data/project-output` (JSON and CSV), `/api/data/integrations/pvgis`, the `project-output` bulk file and the MCP tool project_output_estimate without a `project`. A member’s API key or a signed-in member session gets the same answer as before; anyone else gets 401 `membersOnly`. `/api/data/project-output/{id}` and project_output_estimate with a `project` stay open. Response shapes are unchanged. | /api/data/project-output, /api/data/integrations/{source}, /api/mcp |
| 2026-09-28 | Members only from this release: a member’s API key, or a signed-in member session on the site. Without either the answer is 401 `membersOnly`, with the reason in English and Arabic and a link to join; a key that cannot be checked is 503 `keyUnverified`. The same holds for /data/saudi-renewables.json and for the MCP tools search_projects (the Saudi, captive, GCC and data-centre trackers) and gcc_series. Response shapes are unchanged. | /api/data/atlas, /api/data/resource/grid, /api/data/gcc-series, /api/mcp |
| 2026-09-27 | New `storage` list and `segment=storage`: each Saudi battery storage project with its MWh, stage, report id and page, the sources the report cites and any figure another source gives differently. Existing fields are unchanged. | /api/data/atlas |
| 2026-09-24 | The outlook with its benchmarks and calibration, the five presets to 2050, one scenario by GET or a validated POST (parameters, shocks, and for members or keys a custom path, Monte Carlo and the frontier, counted as five requests), and the backtest. MCP tools oil_outlook and oil_scenario. Each answer carries its source, as-of date and a note that it is modelled. | /api/data/oil-strategy/outlook, /api/data/oil-strategy/presets, /api/data/oil-strategy/scenario, /api/data/oil-strategy/backtest |
| 2026-09-24 | Headlines now come from publishers’ feeds first and GDELT second, every three hours; the response shape is unchanged. | /api/data/news |
| 2026-09-24 | New: GCC clean-energy equipment imports by importer, exporter, product group and year, 2017 onward, from CEPII BACI, as JSON or CSV. | /api/data/equipment-flows |
| 2026-09-24 | Bulk files for every dataset whose licence allows redistribution, with a licence manifest, a strong ETag and a weight of ten requests (a 304 counts as one); 403 with the reason for the rest. A Postman collection generated from the OpenAPI document, which now describes the six webhook events in its webhooks section. The change ledger takes a before cursor and answers nextBefore on a full page. | /api/data/bulk, /api/data/bulk/{file}, /api/postman.json, /api/data/changes |
| 2026-09-24 | The NASA POWER resource for any 0.5° cell and the whole grid; GCC country series from IRENA, Our World in Data and the World Bank; the atlas project layer at locality level; and headlines matched to projects and companies. Each carries its source, licence and as-of date. | /api/data/resource, /api/data/resource/grid, /api/data/gcc-series, /api/data/atlas, /api/data/news |
| 2026-09-24 | Six metered routes: the source catalogue with health, any integrated source’s rows with its own filters, project output estimates with totals by stage, equipment trade aggregates and GCC emissions. CSV where the upstream licence allows; UN Comtrade and PeeringDB answer 403 to a CSV request. | /api/data/integrations, /api/data/integrations/{source}, /api/data/project-output, /api/data/project-output/{id}, /api/data/trade, /api/data/emissions |
| 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.