Skip to content
عربي

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

GET/api/data/projects/sudair-pv-1500-mw
MeteredgetProject
in the pathrequired

A tracker id, e.g. sudair-pv-1500-mw, or a captive segment id, e.g. nadec-haradh-pv.

The key stays in this page, or in this tab if you choose, and is sent only to this site’s API. The code snippets read it from RV_API_KEY and never contain it.

Choose an operation and send it. The answer appears here as a tree, a table and, where the rows allow, a chart.

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

Every route but the Energy Atlas’s layers answers any caller, as it always has. Requests are limited per address, in memory, over ten-minute windows. Responses may be served from the edge cache.

With a free key

A daily allowance that no other caller behind the same address shares, a count of today’s requests on your account page, and responses that always come from the origin. Keys are issued to free members.

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.

CallerRoutesAllowanceCounted by
Without a key/api/status, /api/data/*, /api/brief*, /api/reports*300 requests per 10 minutesAddress, per server instance
Without a key/api/search240 requests per 10 minutesAddress, per server instance
Without a key/api/mcp300 requests per 10 minutesAddress, per server instance
Free keyAll metered routes5,000 requests per UTC dayKey, across all instances
Licensed keyAll metered routesSet by the licenceKey, 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

EndpointWhat it returnsParametersResponse fields
GET /api/status

Metered

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. ok is false only when a source has aged past its refresh cadence. The page at /status renders the same computation.

  • endpoints

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 (required)

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 (required)
  • asOf

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.

  • id (required)
—

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.

  • company (required)
—

Cite from: source, asOf, citation, note

GET /api/data/gcc-projects

Metered

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.

  • country
  • technology
  • status
—

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.

  • id (required)
—

Cite from: source, asOf, citation, note, project.sources

GET /api/data/changes

Metered

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.

  • since
  • domain
  • source
  • limit
  • before

count, nextBefore, changes

Cite from: changes[].sourceId, changes[].occurredOn

GET /api/data/due

Metered

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.

  • days

asOf, windowDays, overdue, items, note

Cite from: items[].publisher, asOf

GET /api/data/power-balance

Metered

Operational capacity against the contracted pipeline and the 2030 target

  • asOf

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-model

Metered

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.

  • scenario
  • horizon

asOf, scenario, levers, sources, context, calibration, summary, months, method

Cite from: sources[], method, asOf

GET /api/data/fleet-outlook

Metered

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/resource

Metered

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 point, cell or project. An area-level answer: a point is snapped to its cell and is not echoed back, and a project is looked up at its locality, never its site.

  • point
  • cell
  • project
—

Cite from: source, asOf, citation, note, licence, acknowledgement, period

GET /api/data/resource/grid

Metered

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; note says how to decode it. Changes only when the grid is rebuilt from NASA POWER. 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 membersOnly, with the reason in English and Arabic and a link to join; when a key cannot be checked the answer is 503 keyUnverified, never an anonymous one.

None
—

Cite from: source, asOf, citation, note, meta.licence, meta.acknowledgement

GET /api/data/gcc-series

Metered

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 membersOnly, with the reason in English and Arabic and a link to join; when a key cannot be checked the answer is 503 keyUnverified, never an anonymous one.

  • metric
  • country
—

Cite from: source, asOf, citation, note, metrics[].source

GET /api/data/news

Metered

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.

  • project
  • company
  • topic
  • limit
—

Cite from: source, asOf, citation, note, headlines[].url, headlines[].domain

GET /api/data/atlas

Metered

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 (place.precision). undated holds the rows with no stated operation year. storage holds every Saudi battery storage project that the Renewable Vision reports RV-SAU-SERIES03-20260912 and RV-SAU-RENEWABLES-GUIDE-20260912 name with a figure, each with its MWh (never added to MW), the report id and page it is read from, the sources the report cites, and any figure another source gives differently (the report’s figure is kept). tariff.usCents is the published tariff in US cents/kWh: for Saudi tracker rows, the levelised cost of electricity as tendered that KAPSARC publishes; for GCC rows, the tariff the procurer or developer published. Data-centre records are placed at city level. Plant sites are not published. 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 membersOnly, with the reason in English and Arabic and a link to join; when a key cannot be checked the answer is 503 keyUnverified, never an anonymous one.

  • segment

projects, undated, storage, dataCentres, sources

Cite from: source, asOf, citation, note, sources, storage[].reports, storage[].sources

GET /api/data/projects

Metered

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 /data/projects: several values of one parameter are alternatives, parameters combine; a place matches everything inside it (place=gcc, place=saudi-arabia/red-sea); ordered facets take ranges (stage=construction..); dates take ranges at any precision (cod=2027..2028). A field the source does not publish is null, never estimated, and notDisclosed counts them. A capacity range is read in one unit family only (the unit named, else the first sector named, else MW, MWp and MWac as stated), and capacity says which and how many rows it left out. format=csv and format=geojson answer the same rows as a file; GeoJSON points are public town or region centroids, or the source’s own point, never finer than 0.1°. 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 membersOnly, with the reason in English and Arabic and a link to join; when a key cannot be checked the answer is 503 keyUnverified, never an anonymous one.

  • place
  • sector
  • tech
  • stage
  • commitment
  • cod
  • fid
  • date
  • investment
  • capacity
  • unit
  • actor
  • developer
  • programme
  • topic
  • source
  • verification
  • q
  • sort
  • format
  • limit
  • offset
—

Cite from: source, asOf, citation, note, projects[].provenance

GET /api/data/largest

Metered

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 membersOnly, with the reason in English and Arabic and a link to join; when a key cannot be checked the answer is 503 keyUnverified, never an anonymous one.

  • l
—

Cite from: Source sheet

GET /api/tenders/xlsx

Metered

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 membersOnly, with the reason in English and Arabic and a link to join; when a key cannot be checked the answer is 503 keyUnverified, never an anonymous one.

  • l
—

Cite from: About sheet

GET /api/data/integrations

Metered

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 csvNotLicensed. A keyed source without its key answers with its state and no rows. PVGIS answers with the project estimates, never the yield cells, and, like /api/data/project-output, answers members only: a member’s API key or a signed-in member session, 401 membersOnly otherwise.

  • source (required)
  • country
  • year
  • sector
  • indicator
  • product
  • partner
  • topic
  • city
  • company
  • format
—

Cite from: source, asOf, citation, note, licence

GET /api/data/project-output

Metered

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 membersOnly, with the reason in English and Arabic and a link to join; when a key cannot be checked the answer is 503 keyUnverified, never an anonymous one.

  • tracker
  • stage
  • country
  • modelled
  • format
—

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

  • id (required)
—

Cite from: source, asOf, citation, note, licence

GET /api/data/trade

Metered

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.

  • country
  • product
  • hs
  • partner
  • year
—

Cite from: source, asOf, citation, note, licence

GET /api/data/equipment-flows

Metered

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. broad marks a row whose HS code holds more than the product named.

  • country
  • partner
  • product
  • year
  • format
—

Cite from: source, asOf, citation, note, licence

GET /api/data/emissions

Metered

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.

  • source
  • country
  • sector
  • year
  • format
—

Cite from: source, asOf, citation, note, licence

GET /api/data/oil-strategy/outlook

Metered

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/presets

Metered

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.

  • shocks
—

Cite from: source, asOf, citation, note

GET /api/data/oil-strategy/scenario

Metered

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.

  • preset
  • shocks
  • format
—

Cite from: source, asOf, citation, note

POST /api/data/oil-strategy/scenario

Metered

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 (X-RateLimit-Cost: 5). Custom paths, the frontier, Monte Carlo and CSV are for members (an API key or a signed-in session).

  • format
  • preset (body)
  • path (body)
  • params (body)
  • shocks (body)
  • monteCarlo (body)
  • frontier (body)
—

Cite from: source, asOf, citation, note

GET /api/data/oil-strategy/backtest

Metered

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/bulk

Metered

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.

  • sizes

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.

  • file (required)

dataset, citation, manifest, excluded, count, rows

Cite from: dataset.licence, dataset.asOf, manifest[].licence, manifest[].attribution, citation

Publications

EndpointWhat it returnsParametersResponse fields
GET /api/brief

Metered

Every weekly brief published, newest first

  • limit

count, briefs

Cite from: briefs[].url, briefs[].issued

GET /api/brief/latest

Metered

The current weekly brief

  • locale

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 (required)
  • locale

week, issued, locale, subject, preheader, blocks, social, summary, url, api

Cite from: url, issued, week

GET /api/reports

Metered

Every published report, newest edition first

  • limit

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 contentUpdated; the live dashboards are separate endpoints and may read differently. The PDF and data pack are member downloads, named by their route.

  • id (required)

report

Cite from: report.url, report.sources, report.definitions, report.contentUpdated

Search

EndpointWhat it returnsParametersResponse fields
GET /api/search

Metered

Ranked matches across datasets, series, insights and projects

  • q (required)
  • locale

groups

Cite from: groups[].items[].href

MCP

EndpointWhat it returnsParametersResponse fields
POST /api/mcp

Metered

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 source, asOf and citation (see the Citation schema), and a note where a figure is modelled. A refusal keeps the JSON-RPC envelope, with the limit or the key error in error.data. Tools and the citation contract: docs/MCP.md.

  • jsonrpc (body, required)
  • id (body)
  • method (body, required)
  • params (body)
—

Cite from: result.structuredContent.source, result.structuredContent.asOf, result.structuredContent.citation

GET /api/mcp

Not metered

No server-sent-event stream

Answers 405 with Allow: POST, DELETE, OPTIONS: the endpoint never pushes.

None
—
DELETE /api/mcp

Not metered

End a session

The endpoint keeps no session, so there is nothing to release.

None
—

Cards and properties

EndpointWhat it returnsParametersResponse 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.

  • card (required)
  • locale
—
GET /api/properties

Not 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.

  • op (required)
  • fluid
  • T
  • P
  • pathway
—

Developer routes

EndpointWhat it returnsParametersResponse fields
GET /api/openapi.json

Not metered

The OpenAPI 3.1 document for every public route

None
—
POST /api/licence-requests

Not 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.

  • name (body, required)
  • email (body, required)
  • organisation (body, required)
  • role (body)
  • licence_type (body, required)
  • datasets (body)
  • use_case (body, required)
  • language (body)
  • consent (body, required)

message

GET /api/postman.json

Not 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 apiKey variable to send a key; leave it empty to call without one.

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.

EndpointWhat it returnsParametersResponse fields
POST /api/privacy-requests

Not 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.

  • type (body, required)
  • email (body, required)
  • details (body)
  • language (body)
  • consent (body, required)

message

POST /api/inquiries

Not metered

The contact form

The only place an enquiry is written. A market-entry enquiry also sends its target markets, home country, offer, references, needs and the size of the work, and is answered once, automatically, by email with an opportunity snapshot from the site’s data. Eight enquiries of any type per address in ten minutes, of which five may be market-entry.

None

message

POST /api/coverage-requests

Not metered

Ask for coverage the site does not have yet

Two kinds of request. sector or place: the "I need this" button on a coming sector or place; the slug must be registered, one request per visitor per item is kept, a signed-in member’s request is linked to their account, and one address may send twenty in ten minutes. request: a request posted to the data-requests board (/data/requests) by a signed-in member, with a title (120 characters at most), details (1,000), one to three registered sectors and places, up to three topics, how soon it is needed and how it is signed; it is shown once an admin approves it. Five per member an hour, ten per address in ten minutes.

  • kind (body, required)
  • slug (body)
  • title (body)
  • details (body)
  • sectors (body)
  • places (body)
  • topics (body)
  • urgency (body)
  • attribution (body)
  • locale (body)

message

POST /api/coverage-requests/{id}/vote

Not 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. Only approved requests take votes. Sixty votes and unvotes per member in ten minutes.

  • id (required)
  • voted (body)

voted, votes

POST /api/coverage-requests/{id}/brief

Not 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.

  • id (required)
  • note (body)
  • locale (body)

already

POST /api/subscribe

Not metered

Join the RV Plus and RV X materials list, with double opt-in

None

message

GET /api/subscribe/confirm

Not metered

The confirmation link: sends the visitor to the confirmation page

None
—
POST /api/subscribe/confirm

Not metered

Confirm a subscription from the confirmation page

None
—
GET /api/subscribe/unsubscribe

Not metered

The unsubscribe link: opens the page that confirms the removal

None
—
POST /api/subscribe/unsubscribe

Not metered

Leave the list, from the page or by one-click from a mail client

None

message

GET /api/subscribe/weekly

Not metered

Whether the signed-in member already receives the weekly update

None

message

POST /api/subscribe/weekly

Not 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/read

Not metered

Read a signed-in member's CV (a PDF) into the application's fields

None

message

POST /api/experts/apply

Not metered

Apply as a specialist (a signed-in member)

None

message

POST /api/experts/briefs

Not metered

Send a question to Renewable Vision's analysts

None

message

POST /api/ask

Not 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/assistant

Not metered

Whether the site assistant can answer now (docs/ASSISTANT.md)

None

ready

POST /api/assistant

Not 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 done. 20 questions in 10 minutes and 120 a day per address, and a site-wide daily cap.

None
—
GET /api/discover

Not 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.

  • locale
  • path
—
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.

  • id (required)
—

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.

Postman collection · OpenAPI 3.1 document

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.

EventSent when
project.stage_changedA project’s stage changed in a register or tracker.
project.addedA project was added to a register or tracker.
project.updatedA project’s capacity, date or listing changed.
dataset.refreshedA register, series, tracker or source was refreshed from its publisher.
news.mentionA headline named a tracked project or company.
brief.publishedThe 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

All datasets, their sizes and licences

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.

  1. Name the original publisher given in the response, and Renewable Vision, with the page the response links to.
  2. Give the date the figure is true as of, from the response, not the date you fetched it.
  3. Keep a modelled figure labelled as modelled. The response says which figures are.
  4. Third-party series keep their publishers’ licences. Where the upstream terms are non-commercial, so is any reuse.
  5. Renewable Vision compilations are CC BY-NC 4.0 unless a licence says otherwise.

Licences and upstream terms · Terms of Use

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

DateChangeRoutes
2026-10-01Members 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-28Members 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-27New `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-24The 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-24Headlines now come from publishers’ feeds first and GDELT second, every three hours; the response shape is unchanged./api/data/news
2026-09-24New: 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-24Bulk 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-24The 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-24Six 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-24The 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-24Optional 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-24One tracker project and one company as JSON, with the fields to cite./api/data/projects/{id}, /api/data/companies/{company}
2026-09-22The Model Context Protocol endpoint./api/mcp
2026-09-22The report library as JSON./api/reports, /api/reports/{id}
2026-09-21The weekly brief as JSON, one permanent address per ISO week./api/brief, /api/brief/latest, /api/brief/{week}
2026-09-20The 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-14The oil balance model, with named scenarios and every lever settable by query./api/data/oil-model
2026-09-13Data freshness, site search and share cards./api/status, /api/search, /api/cards/{card}
2026-09-12Mirrored 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.