Titan Weather API
The storm record, over the wire.
Everything the application does, your systems can do: compile citation-backed weather reports, keep a book of watched addresses and territories in step, hear about storms the hour they enter the record, and draw the archive on your own map.
Base URL
https://titanweather.com/api/v1
Every request carries a bearer token issued from
Settings,
scoped to exactly what it should be able to do. The samples on every page read it from
TITAN_API_TOKEN
so a copied snippet never carries a secret.
curl -X POST https://titanweather.com/api/v1/reports \
-H "Authorization: Bearer $TITAN_API_TOKEN" \
-H "Idempotency-Key: claim-48211-first-report" \
-H "Content-Type: application/json" \
-d '{
"address": "1428 Cedar Ridge Dr, Plano, TX 75023",
"window_start": "2016-08-23",
"window_end": "2026-08-23",
"radius_miles": 5
}'
$titan = new \GuzzleHttp\Client([
'base_uri' => 'https://titanweather.com',
'headers' => ['Authorization' => 'Bearer '.getenv('TITAN_API_TOKEN')],
]);
$response = $titan->post('/api/v1/reports', [
'headers' => ['Idempotency-Key' => 'claim-48211-first-report'],
'json' => [
'address' => '1428 Cedar Ridge Dr, Plano, TX 75023',
'window_start' => '2016-08-23',
'window_end' => '2026-08-23',
'radius_miles' => 5,
],
]);
$report = json_decode((string) $response->getBody(), true);
const response = await fetch('https://titanweather.com/api/v1/reports', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TITAN_API_TOKEN}`,
'Idempotency-Key': 'claim-48211-first-report',
'Content-Type': 'application/json',
},
body: JSON.stringify({
address: '1428 Cedar Ridge Dr, Plano, TX 75023',
window_start: '2016-08-23',
window_end: '2026-08-23',
radius_miles: 5,
}),
});
const report = await response.json();
import os
import requests
response = requests.post(
"https://titanweather.com/api/v1/reports",
headers={
"Authorization": f"Bearer {os.environ['TITAN_API_TOKEN']}",
"Idempotency-Key": "claim-48211-first-report",
},
json={
"address": "1428 Cedar Ridge Dr, Plano, TX 75023",
"window_start": "2016-08-23",
"window_end": "2026-08-23",
"radius_miles": 5,
},
)
report = response.json()
{
"data": {
"id": "0198b6c1-22e4-7f88-a1c9-5d40b7e2913a",
"object": "report",
"status": "pending",
"address": {
"query": "1428 Cedar Ridge Dr, Plano, TX 75023",
"street": null,
"city": null,
"state": null,
"postal_code": null,
"county": null,
"latitude": null,
"longitude": null
},
"window": {
"start": "2016-08-23",
"end": "2026-08-23",
"radius_miles": 5,
"narrowed_by_plan": false
},
"property_id": null,
"event_count": 0,
"observation_count": 0,
"coverage": {
"complete": false,
"provisional": false,
"summary": null,
"sources_searched": []
},
"headline": null,
"findings": null,
"failure_reason": null,
"plan_tier": "professional",
"layout": {
"sections": [
"summary",
"findings",
"map",
"events"
],
"imagery": [
"aerial",
"street_view"
],
"elements": [
"key_figures"
],
"map": null
},
"created_at": "2026-08-23T14:02:11Z",
"generated_at": null,
"pdf": {
"status": null,
"rendered_at": null
},
"links": {
"self": "https://titanweather.com/api/v1/reports/0198b6c1-22e4-7f88-a1c9-5d40b7e2913a",
"events": null,
"pdf": null
}
}
}
The reference
55 endpoints · generated from the routes
Additive, always
Within v1, fields are added and never repurposed. A parser written today keeps working; keys that are not yet earned are null, never missing.
Refusals you can tell apart
A missing credential, a plan without room, a token without the ability and an id that is not yours each answer differently, so your handling can too. Errors.
Retries that cannot double-bill
Send an Idempotency-Key
with any creating request and a timeout cannot create or charge twice.
Idempotency.