Skip to content
Renewable Vision

Developers

The data behind the dashboards, for your own code.

Every register, series and computed figure the dashboards read is served from this domain as JSON, and over the Model Context Protocol for AI assistants. No key is needed. A free key adds a daily allowance of its own and a count of what it has used.

Overview

What the API serves.

The same rows the dashboards draw, with the publisher, unit and period on every series and the source on every computed figure.

Official registers
The Principal Buyer’s renewable project register, the SHARAKAT water portfolio and the Saudi Water Authority open-data catalogue, mirrored weekly, and each rebuilt for a past date from the change ledger.
Mirrored series
30 series from the KAPSARC Energy Data Portal and the publishers each one names: load, capacity, tariffs, desalination, oil balance, prices and more, every point with its period, region and unit.
Change ledger and schedule
Every row-level change in a register or series with the value on both sides, and what is expected to refresh next.
Model outputs
The power balance against the 2030 target, the oil balance model under named scenarios, and the week ahead for the solar fleet. Modelled figures say so in the response.
Publications
The weekly brief, one permanent address per ISO week, and the report library with each report’s definitions and sources.

Without a key

Every route 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. Send it in one of two headers.

Create a key on your account page. It is shown once, when you create it: store it where your code reads secrets, and revoke it from the same page if it is exposed.

Either header

Authorization: Bearer rv_live_…
X-API-Key: rv_live_…

A key that is sent must be valid

A request that carries a key is answered with 401 when the key is malformed, unknown or revoked, rather than quietly served without it. The body names the reason. Remove the header to fall back to the anonymous limit.

Keep keys on a server. A key in browser code is readable by anyone who opens the page.

Limits

Allowances by caller.

Limits keep the service available to every caller. Anonymous calls are limited per address over ten minutes; a key is limited per UTC day.

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

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

count, 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

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

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

None

message

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

POST /api/experts/apply

Not metered

Apply to join the Expert Panel

None

message

POST /api/experts/briefs

Not metered

Ask the Expert Panel for a written opinion

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/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');

MCP

Connect an AI assistant.

The same data is a Model Context Protocol server over Streamable HTTP. An assistant that accepts a remote MCP server needs only the URL. Every tool result carries the publisher, the date its figures are true as of, and the page to cite.

POST https://renewablevision.com.sa/api/mcp

Client configuration

{
  "mcpServers": {
    "renewable-vision": {
      "type": "http",
      "url": "https://renewablevision.com.sa/api/mcp",
      "headers": {
        "Authorization": "Bearer rv_live_…"
      }
    }
  }
}

To count an assistant’s calls against your key, add the key as an Authorization header in the client’s configuration. Without it the endpoint answers under the per-address limit.

Discovery record: /.well-known/mcp/server.json

Attribution

Cite the publisher and Renewable Vision.

Every figure has two sources to name: the body that published it, and this platform, which compiled, mirrored or computed it.

  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

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