California SP15 Day-Ahead Price API
You need a reliable way to fetch the latest California SP15 Day-Ahead power price and wire it into your finance workflow. By the end of this guide, you will query the Energy API for CAISO SP15 Day-Ahead (symbol CAISO_SP15_DA), parse the latest price in USD/MWh, handle base currency details, and productionize the call with caching and scheduling.
What you will build: fetch and parse CAISO_SP15_DA
This walkthrough focuses on one symbol: California SP15 Day-Ahead (CAISO_SP15_DA). You will:
- Authenticate with the api_key query parameter.
- Call GET https://energy-api.com/api/v1/latest with symbols=CAISO_SP15_DA.
- Read rates.CAISO_SP15_DA, dates.CAISO_SP15_DA, currencies.CAISO_SP15_DA.
- Integrate latency-aware caching and retry behavior suitable for finance-grade systems.
Plan notes: Starter is $19.99/mo with a 7-day trial capped at 50 calls. There is no MCP host to configure.
Symbol, units, and base currency
- Symbol name: California SP15 Day-Ahead
- Symbol code: CAISO_SP15_DA
- Unit: USD/MWh (prices are per megawatt-hour, denominated in USD)
- Base currency: The response includes a top-level base field. For this symbol, the base may be USD. The API can also return MIXED when multiple instruments with different currencies are queried. In this guide, we request a single symbol.
Do not pass base=USD in queries that include TTF_GAS or EUA_CO2 (those instruments can require MIXED). This post stays on CAISO_SP15_DA only.
Authentication and endpoint
Use the api_key query parameter. Replace YOUR_API_KEY with your own key after you register. No headers are required for auth in this flow.
- HTTP method: GET
- Endpoint: https://energy-api.com/api/v1/latest
- Parameters: symbols (comma-separated list), api_key
If you are building a billing-safe PoC: the trial is 7 days / 50 calls. Budget your scheduler accordingly.
Official cURL to get started
Copy and run this as-is, inserting your API key:
curl -G "https://energy-api.com/api/v1/latest" --data-urlencode "symbols=CAISO_SP15_DA" --data-urlencode "api_key=YOUR_API_KEY"
Official sample JSON (for reference format)
This official sample demonstrates the response schema (fields and nesting). It shows a different symbol (OMIE_ES_DA) but the structure matches what you will parse for CAISO_SP15_DA.
{
"success": true,
"date": "2026-10-06",
"base": "EUR",
"rates": {
"OMIE_ES_DA": 206.6754
},
"dates": {
"OMIE_ES_DA": "2026-10-06"
},
"currencies": {
"OMIE_ES_DA": "EUR"
},
"base_filter_note": null
}
Real API response for CAISO_SP15_DA
This is an actual response for California SP15 Day-Ahead. Use it to validate your field parsing logic for rates, dates, and currencies.
{"success":true,"date":"2026-10-04","base":"USD","rates":{"CAISO_SP15_DA":56.2213},"dates":{"CAISO_SP15_DA":"2026-10-04"},"currencies":{"CAISO_SP15_DA":"USD"},"base_filter_note":null}
What you need for finance pipelines:
- rates.CAISO_SP15_DA: latest price float (USD/MWh).
- dates.CAISO_SP15_DA: pricing date string (ISO-8601).
- currencies.CAISO_SP15_DA: currency code (USD).
- date (top-level): server-reported anchor date for this response batch.
- base: base currency for the response. For a single USD-denominated symbol, you should expect USD. When mixing instruments, this may be MIXED.
Repeat-call reference JSON (same symbol)
When polling within the same trading day without a new publication, you may see the same price snapshot. Your parser should be idempotent against repeats like the example below.
{"success":true,"date":"2026-10-04","base":"USD","rates":{"CAISO_SP15_DA":56.2213},"dates":{"CAISO_SP15_DA":"2026-10-04"},"currencies":{"CAISO_SP15_DA":"USD"},"base_filter_note":null}
Python example: fetch and parse CAISO_SP15_DA
This example hits the latest endpoint, validates the success flag, and extracts the price, date, and currency for CAISO_SP15_DA.
import os
import sys
import json
import time
import urllib.parse
import urllib.request
API_URL = "https://energy-api.com/api/v1/latest"
SYMBOL = "CAISO_SP15_DA"
API_KEY = os.environ.get("ENERGY_API_KEY", "YOUR_API_KEY")
def fetch_latest(symbol):
params = {
"symbols": symbol,
"api_key": API_KEY
}
url = API_URL + "?" + urllib.parse.urlencode(params)
req = urllib.request.Request(url, method="GET")
with urllib.request.urlopen(req, timeout=15) as resp:
body = resp.read()
data = json.loads(body.decode("utf-8"))
if not data.get("success"):
raise RuntimeError("API returned success=false")
# Required fields
rates = data.get("rates", {})
dates = data.get("dates", {})
currencies = data.get("currencies", {})
if symbol not in rates or symbol not in dates or symbol not in currencies:
raise KeyError("Expected fields missing for symbol: " + symbol)
return {
"symbol": symbol,
"price": float(rates[symbol]),
"price_date": dates[symbol],
"currency": currencies[symbol],
"base": data.get("base"),
"batch_date": data.get("date")
}
def main():
try:
latest = fetch_latest(SYMBOL)
except Exception as e:
print("Fetch failed:", e, file=sys.stderr)
sys.exit(1)
print("Symbol:", latest["symbol"])
print("Price:", latest["price"], latest["currency"], "(unit: USD/MWh)")
print("Price date:", latest["price_date"])
print("Batch date:", latest["batch_date"])
print("Base:", latest["base"])
if __name__ == "__main__":
main()
JavaScript example: node fetch with retry and basic cache
The snippet uses a simple in-memory cache that expires after 5 minutes. Adjust the TTL to your scheduling interval.
import https from "https";
import { URL } from "url";
const API_URL = "https://energy-api.com/api/v1/latest";
const SYMBOL = "CAISO_SP15_DA";
const API_KEY = process.env.ENERGY_API_KEY || "YOUR_API_KEY";
let cache = { value: null, expiresAt: 0 };
function httpGetJson(urlStr) {
return new Promise((resolve, reject) => {
const url = new URL(urlStr);
https.get(url, (res) => {
let data = "";
res.on("data", (chunk) => (data += chunk));
res.on("end", () => {
try {
resolve(JSON.parse(data));
} catch (err) {
reject(err);
}
});
}).on("error", reject);
});
}
async function fetchLatest(symbol) {
const now = Date.now();
if (cache.value && cache.expiresAt > now) {
return cache.value;
}
const url = new URL(API_URL);
url.searchParams.set("symbols", symbol);
url.searchParams.set("api_key", API_KEY);
// naive retry x3
let lastErr;
for (let i = 0; i < 3; i++) {
try {
const json = await httpGetJson(url.toString());
if (!json.success) throw new Error("API success=false");
const price = json.rates?.[symbol];
const pDate = json.dates?.[symbol];
const ccy = json.currencies?.[symbol];
if (price == null || !pDate || !ccy) {
throw new Error("Missing fields for " + symbol);
}
const result = {
symbol,
price: Number(price),
priceDate: pDate,
currency: ccy,
unit: "USD/MWh",
base: json.base,
batchDate: json.date
};
cache = { value: result, expiresAt: now + 5 * 60 * 1000 };
return result;
} catch (err) {
lastErr = err;
await new Promise((r) => setTimeout(r, 500 * (i + 1)));
}
}
throw lastErr;
}
fetchLatest(SYMBOL)
.then((d) => console.log(d))
.catch((e) => {
console.error("Fetch failed:", e.message);
process.exit(1);
});
Field-by-field: what to read and store
Persist only what you will use operationally. For finance use cases (marking, PnL transforms, settlement lookups):
- rates.CAISO_SP15_DA: decimal; store as numeric with at least 4 decimals to avoid rounding artifacts.
- dates.CAISO_SP15_DA: ISO date; treat as local market date, not necessarily UTC midnight alignment. Keep as string or date-only.
- currencies.CAISO_SP15_DA: "USD"; used for portfolio currency sanity checks.
- base: for single-symbol fetches here, expect "USD". For mixed baskets, you may see "MIXED". No conversion is applied by default.
- date (top-level): anchor date of the response batch. Useful for monitoring snapshots across subscriptions.
End-to-end timeline and non-trading days
Day-ahead power prices are not published 24/7 like FX quotes. Depending on market calendars and weekends/holidays, you can see flat prints across multiple calendar days if there is no new publication. In your job scheduler:
- Use a once-per-day fetch aligned with the expected publication window, plus a small back-off retry window.
- On weekends/holidays, expect unchanged values; do not fail the pipeline on repeated dates.
- Log the dates.CAISO_SP15_DA field so you can verify whether a new value was published.
Caching, idempotency, and monitoring
To avoid hitting trial or plan limits prematurely (7 days / 50 calls on trial; Starter $19.99/mo), add a short TTL cache in your service. If the response date and symbol date have not changed, reuse the cached value downstream. Recommended checks:
- Cache key: CAISO_SP15_DA + dates.CAISO_SP15_DA.
- Emit a metric when a new pricing date appears; wire alerts if no update is seen past your expected window.
- Guardrails: if base is not "USD" for this single-symbol call, log a warning and proceed—MIXED should not occur in a single-symbol request.
Common pitfalls and how to avoid them
- Passing unsupported base filters: Do not pass base=USD when you also need TTF_GAS or EUA_CO2. This post focuses on CAISO_SP15_DA only, so omit base entirely.
- Assuming intraday volatility: Day-ahead settlement prints may not change intraday. Use scheduler logic instead of tight polling loops.
- Ignoring units: Keep the unit USD/MWh attached to your data model to prevent mixing with USD/MMBtu or EUR/MWh instruments.
- Silently swallowing success=false: Always check the success flag and handle errors early.
Production runbook: retries, backoff, and data quality
Build predictable resilience into your integration:
- Retries: 2–3 attempts with incremental backoff (e.g., 0.5s, 1s, 2s). Avoid hammering the API; align backoff with your SLA.
- Timeouts: 10–15 seconds per request is reasonable for this endpoint.
- Circuit breaker: If repeated failures occur, open a short circuit (e.g., 2 minutes) and serve cached data to downstream systems.
- Data health checks: Ensure price is a finite number; reject NaN or null values.
End-to-end test using the official cURL
Run the following cURL to confirm your API key and symbol mapping. This is a copy-paste from the official sample and doubles as a smoke test for network and auth paths.
curl -G "https://energy-api.com/api/v1/latest" --data-urlencode "symbols=CAISO_SP15_DA" --data-urlencode "api_key=YOUR_API_KEY"
JSON example recap: structure you will parse
Below are three complete JSON examples referenced in this article so you can unit-test your parser without live calls.
Official sample structure (different symbol, same schema)
{
"success": true,
"date": "2026-10-06",
"base": "EUR",
"rates": {
"OMIE_ES_DA": 206.6754
},
"dates": {
"OMIE_ES_DA": "2026-10-06"
},
"currencies": {
"OMIE_ES_DA": "EUR"
},
"base_filter_note": null
}
CAISO_SP15_DA real response
{"success":true,"date":"2026-10-04","base":"USD","rates":{"CAISO_SP15_DA":56.2213},"dates":{"CAISO_SP15_DA":"2026-10-04"},"currencies":{"CAISO_SP15_DA":"USD"},"base_filter_note":null}
Repeat snapshot example (same fields, unchanged)
{"success":true,"date":"2026-10-04","base":"USD","rates":{"CAISO_SP15_DA":56.2213},"dates":{"CAISO_SP15_DA":"2026-10-04"},"currencies":{"CAISO_SP15_DA":"USD"},"base_filter_note":null}
Operational notes for finance teams
Downstream valuation and risk flows depend on determinism. Treat the symbol and unit as a typed key (CAISO_SP15_DA, USD/MWh). Store both the price_date (dates.CAISO_SP15_DA) and the batch_date (top-level date) to trace lineage. When reconciling, prefer the symbol-specific date over the batch date.
If you must show a dashboard, display both the price and the unit clearly, and optionally show the pricing date next to the value to avoid confusion on weekends or holidays.
FAQ
Q: Which fields should I rely on for the latest CAISO_SP15_DA price?
A: Read rates.CAISO_SP15_DA for the numeric price, dates.CAISO_SP15_DA for the price date, and currencies.CAISO_SP15_DA for currency. The unit is USD/MWh.
Q: Do I need to pass a base parameter?
A: No. For CAISO_SP15_DA, omit base. Only note that if you later include TTF_GAS or EUA_CO2 in the same call, you must not force base=USD; let the API return MIXED.
Q: How often should I poll the latest endpoint?
A: Once per day aligned with publication is typical for day-ahead power. Use a short retry window and cache results to avoid unnecessary calls.
Q: How do I authenticate?
A: Include api_key as a query parameter. Example: api_key=YOUR_API_KEY. No special headers are required for this guide.
Q: What are the trial and starter plan details?
A: Trial is 7 days with 50 calls. Starter is $19.99/month. There is no MCP host to configure.
Next steps
Register for an API key and ship your first integration within minutes. See the parameters and field definitions in the docs before moving to production.
Ready to get started?
Get your API key and start querying energy commodity prices in minutes.
Get API KeyRelated posts
Unlock the power of the California NP15 Day-Ahead Price API with our finance API guide. Learn to query, parse,...
Read more →
Discover how to leverage Energy API for backtesting renewable hedging strategies. Simulate volatility and opti...
Read more →
Discover how to design a reproducible backtesting framework for power trading strategies using Energy API time...
Read more →
Discover how Energy API can automate regulatory compliance reporting for utilities, streamlining FERC and EU R...
Read more →
Discover how to build a geo-fenced distributed energy resource orchestrator using Energy API and MQTT for low-...
Read more →