JSON stands for JavaScript Object Notation. Despite the name, it has nothing to do with JavaScript in practice — it is simply a text format for storing and transmitting structured data. Any programming language can read it. You can also open it in a plain text editor.
A JSON file looks like this:
{
"chain": "bitcoin",
"date": "2026-04-13",
"status_label": "HEATING",
"confidence_score": 0.82,
"drivers": [
{ "metric": "tx_count_daily", "z_robust": 1.74 },
{ "metric": "median_tx_fee_native", "z_robust": -2.02 }
]
}Wrap a group of named fields. Each field has a name (in quotes) and a value.
Wrap a list of items. Items can be numbers, text, or other objects.
Separates a field name from its value. Comma separates multiple fields.
API stands for Application Programming Interface. In plain language: it is a way for your computer to ask a server for data and get a structured response back. Instead of opening a webpage and reading it with your eyes, your script sends a request and receives machine-readable data directly.
An API key is a long string of characters that identifies you as a subscriber. You include it in every request so the server knows who you are and what you are entitled to access. Think of it like a key card — you swipe it every time you enter, and the system knows which rooms you are allowed into.
ta_live_2c627b2852556cf57de58ae97d6476e27660cf1ebe9348a2API responses may include X-RateLimit-Limit, X-RateLimit-Remaining, X-DailyQuota-Limit, X-DailyQuota-Remaining, and Retry-After headers. If you receive a 429 response, slow down or wait until the relevant reset time before retrying.
When you subscribe to Urd Atlas, you get API access to structured JSON files. There are four published JSON genres. Gold, Derived, and Meta are the technical data layers; Briefs are the readable, direct-use JSON layer built from the latest Meta context. Here is the complete picture:
| Genre | What it contains | Best for |
|---|---|---|
| Gold | Raw daily chain metrics in native units. Transaction counts, fees, block times, active addresses. | Your own calculations, raw data analysis |
| Meta | The regime label (STABLE/HEATING/CONGESTED/CHEAP), confidence score, scorecard, and ranked driver signals. | Dashboards, alerts, research conditioning |
| Derived | 7-day and 30-day rolling averages for every Gold metric. | Trend charts, smoothed analysis |
| Briefs | Short descriptive JSON summaries of the latest regime context, including what changed, what drove it, and the non-advisory guardrails. | Fast reading, reporting, onboarding, and non-pipeline workflows |
If your entitled chain is bitcoin, you can fetch:
/api/v1/files/gold/bitcoin/latest.json
/api/v1/files/meta/bitcoin/latest.json
/api/v1/files/derived/bitcoin/latest.json
/api/v1/files/briefs/chains/bitcoin/latest.json
/api/v1/files/gold/bitcoin/7d/latest.json
/api/v1/files/meta/bitcoin/7d/latest.json
/api/v1/files/derived/bitcoin/7d/latest.json
/api/v1/files/gold/bitcoin/30d/latest.json
/api/v1/files/meta/bitcoin/30d/latest.json
/api/v1/files/derived/bitcoin/30d/latest.json
/api/v1/files/gold/bitcoin/90d/latest.json
/api/v1/files/meta/bitcoin/90d/latest.json
/api/v1/files/derived/bitcoin/90d/latest.jsonThat is 13 current files in this example: Gold, Meta, and Derived across 4 windows, plus the latest per-chain Briefs JSON.
Same structure, but for all four chains and 6 windows (adds 180d and 365d):
/api/v1/files/meta/bitcoin/latest.json
/api/v1/files/meta/ethereum/latest.json
/api/v1/files/meta/arbitrum/latest.json
/api/v1/files/meta/base/latest.json
/api/v1/files/briefs/chains/bitcoin/latest.json
/api/v1/files/briefs/chains/ethereum/latest.json
/api/v1/files/briefs/chains/arbitrum/latest.json
/api/v1/files/briefs/chains/base/latest.json
/api/v1/files/meta/bitcoin/365d/latest.json
/api/v1/files/meta/ethereum/365d/latest.json
...and so on for gold and derived. Briefs are published as latest per-chain files.That is 72 technical window files — 3 technical genres × 4 chains × 6 windows — plus 4 latest per-chain Briefs JSON files.
This is where most new subscribers get confused. The URL structure looks like this:
/api/v1/files/{genre}/{chain}/{window}/latest.json
/api/v1/files/{genre}/{chain}/latest.json
/api/v1/files/briefs/chains/{chain}/latest.json{
"chain": "bitcoin",
"window": "90d",
"days": [
{ "date": "2026-01-14", "status_label": "STABLE", "confidence_score": 0.79, ... },
{ "date": "2026-01-15", "status_label": "STABLE", "confidence_score": 0.81, ... },
{ "date": "2026-01-16", "status_label": "HEATING", "confidence_score": 0.76, ... },
...90 records total
]
}latest.json (without a window). One request per chain, one file, always fresh.90d/latest.jsononce when you sign up to get your history, then fetch latest.jsondaily to add one new day at a time.90d for Single Chain,365d for Research) to get everything in one request.| Window token | What it contains | Available on |
|---|---|---|
| latest | Most recent single day only | Single Chain + Research |
| 7d | Last 7 days as an array | Single Chain + Research |
| 30d | Last 30 days as an array | Single Chain + Research |
| 90d | Last 90 days as an array | Single Chain + Research |
| 180d | Last 180 days as an array | Research only |
| 365d | Last 365 days as an array | Research only |
You open a terminal and run one command. You get the file. You repeat when you want new data. No setup, no scheduler, no code. Good for: occasional checks, one-off analysis, getting started.
You write a small script that checks for new data and downloads it if found. You schedule it to run every day. Good for: daily archives, feeding a dashboard, keeping data fresh without thinking about it.
curl -H "X-API-Key: YOUR_KEY" \
https://urdatlas.com/api/v1/files/meta/bitcoin/latest.json-o filename.json to save to disk:curl -H "X-API-Key: YOUR_KEY" \
https://urdatlas.com/api/v1/files/meta/bitcoin/latest.json \
-o bitcoin_meta_latest.jsonThe API key does not permanently store a separate plan. Each request is checked against the account's current subscription status, entitled chain, allowed file genres, and allowed history window. If the subscription is inactive, subscriber file delivery is blocked even if the key string itself still exists.
Each account can have up to two non-revoked API keys. Active and suspended keys count toward this limit. Revoked keys do not count. To create a third key, revoke an existing key first from Dashboard.
Single Chain keys can fetch Gold, Meta, Derived, and Briefs for the selected chain only. Available windows are latest, 7d, 30d, and 90d. Requests for another chain or for 180d/365d return a forbidden response.
Research keys can fetch Gold, Meta, Derived, and Briefs for Bitcoin, Ethereum, Arbitrum, and Base. Available windows are latest, 7d, 30d, 90d, 180d, and 365d.
There are several ways to make an HTTP request. Pick the one that matches what you already have installed.
curl is usually pre-installed. Open any terminal and run:
curl -H "X-API-Key: YOUR_KEY_HERE" https://urdatlas.com/api/v1/files/meta/bitcoin/latest.jsonTo save to a file instead of printing:
curl -H "X-API-Key: YOUR_KEY_HERE" https://urdatlas.com/api/v1/files/meta/bitcoin/latest.json -o meta_bitcoin_latest.jsonInstall requests once: pip install requests
import requests, json
API_KEY = "YOUR_KEY_HERE"
url = "https://urdatlas.com/api/v1/files/meta/bitcoin/latest.json"
response = requests.get(url, headers={"X-API-Key": API_KEY}, timeout=30)
response.raise_for_status() # raises an error if request failed
data = response.json()
print(data["status"]["label"]) # prints e.g. HEATING
print(data["confidence"]["confidence_score"]) # prints e.g. 0.82
# Save to file
with open("meta_bitcoin_latest.json", "w") as f:
json.dump(data, f, indent=2)$key = "YOUR_KEY_HERE"
$url = "https://urdatlas.com/api/v1/files/meta/bitcoin/latest.json"
$headers = @{ "X-API-Key" = $key }
$response = Invoke-RestMethod -Uri $url -Headers $headers
$response | ConvertTo-Json -Depth 10 | Out-File -FilePath "meta_bitcoin_latest.json"You do not need much. Here is the minimum and the recommended setup.
Good enough for: manual daily fetching and reading the output.
Good for: building your own analysis, charts, and automation.
Good for: spreadsheet users who want structured data without writing code.
Once you have JSON files on your computer, here are the most common things new subscribers want to do with them.
import json
with open("meta_bitcoin_latest.json") as f:
data = json.load(f)
label = data["status"]["label"] # e.g. "HEATING"
confidence = data["confidence"]["confidence_score"] # e.g. 0.82
date = data["date"] # e.g. "2026-04-13"
print(f"{date}: {label} (confidence: {confidence:.2f})")import json
import pandas as pd
with open("meta_bitcoin_90d.json") as f:
data = json.load(f)
# The days array contains one record per day
rows = []
for day in data["days"]:
rows.append({
"date": day["date"],
"label": day["status"]["label"],
"confidence": day["confidence"]["confidence_score"],
})
df = pd.DataFrame(rows)
df["date"] = pd.to_datetime(df["date"])
df = df.sort_values("date")
print(df.tail(10)) # show last 10 rows
print(df["label"].value_counts()) # how many days each label appearedimport requests, pandas as pd
API_KEY = "YOUR_KEY_HERE"
CHAINS = ["bitcoin", "ethereum", "arbitrum", "base"]
rows = []
for chain in CHAINS:
url = f"https://urdatlas.com/api/v1/files/meta/{chain}/latest.json"
data = requests.get(url, headers={"X-API-Key": API_KEY}).json()
rows.append({
"chain": chain,
"label": data["status"]["label"],
"confidence": data["confidence"]["confidence_score"],
"date": data["date"],
})
df = pd.DataFrame(rows)
print(df.to_string(index=False))This works best with the window files (90d, 365d) because they contain an array of daily records. The latest.json file contains only one day.
Instead of fetching blindly every day, the script checks the public status endpoint first. If the published date has changed since the last fetch, it downloads the new file. This avoids unnecessary requests and makes logs easy to read.
import json, os
from pathlib import Path
import requests
BASE_URL = "https://urdatlas.com"
API_KEY = os.environ["URD_ATLAS_API_KEY"] # set this as environment variable — never hardcode
CHAIN = "bitcoin" # change to your entitled chain
GENRE = "meta" # gold, meta, derived, or briefs
DATA_DIR = Path("urdatlas_data") / GENRE / CHAIN
STATE_FILE = Path("urdatlas_state") / f"{CHAIN}_{GENRE}.json"
DATA_DIR.mkdir(parents=True, exist_ok=True)
STATE_FILE.parent.mkdir(parents=True, exist_ok=True)
def get_last_seen():
if not STATE_FILE.exists():
return None
return json.loads(STATE_FILE.read_text())
def save_last_seen(as_of):
STATE_FILE.write_text(json.dumps({"as_of": as_of}))
def check_status():
r = requests.get(f"{BASE_URL}/api/v1/status", timeout=30)
r.raise_for_status()
chains = r.json().get("chains", [])
for row in chains:
if row.get("chain") == CHAIN:
return row.get("as_of")
raise RuntimeError(f"Chain {CHAIN} not found in status")
def fetch_file():
path = f"briefs/chains/{CHAIN}/latest.json" if GENRE == "briefs" else f"{GENRE}/{CHAIN}/latest.json"
url = f"{BASE_URL}/api/v1/files/{path}"
r = requests.get(url, headers={"X-API-Key": API_KEY}, timeout=30)
r.raise_for_status()
return r.json()
def main():
current_as_of = check_status()
last_seen = get_last_seen()
if last_seen and last_seen.get("as_of") == current_as_of:
print(f"No new data. Still at {current_as_of}.")
return
data = fetch_file()
out = DATA_DIR / f"{current_as_of}_latest.json"
out.write_text(json.dumps(data, indent=2))
save_last_seen(current_as_of)
print(f"Saved: {out}")
if __name__ == "__main__":
main()set URD_ATLAS_API_KEY=your_key_here
python fetch_urdatlas.pyexport URD_ATLAS_API_KEY=your_key_here
python3 fetch_urdatlas.pyC:\Python311\python.exeC:\scripts\fetch_urdatlas.pyOpen Terminal and type crontab -e, then add:
# Run every day at 14:00 and 18:00 (catches most publication windows)
0 14,18 * * * URD_ATLAS_API_KEY=your_key /usr/bin/python3 /home/you/fetch_urdatlas.py >> /home/you/urdatlas.log 2>&1Save and exit. Cron runs the script automatically. Check the log file to see what happened.
X-API-Key and that the key value is correct. API keys are case-sensitive.