One endpoint. No key, no signup, no rate-limit fluff. Returns the same numbers the calculator shows you, as JSON.
GET https://dfetcher.com/api/calculate
CORS-open. Cached at the edge for 60 seconds. Returns application/json.
curl 'https://dfetcher.com/api/calculate?lat=41.24&lon=-81.84&skin=3&target=1000'
| Name | Type | Default | Notes |
|---|---|---|---|
| lat | number | — | Required. -90 to 90. |
| lon | number | — | Required. -180 to 180. |
| skin | integer 1–6 | 3 | Fitzpatrick skin type. |
| area | number 0–1 | 0.25 | Exposed body fraction. 0.10 face+hands, 0.25 arms+face, 0.85 near-full body. |
| scalp | enum | hair | hair | bald. Bald adds ~3.5% BSA. |
| age | integer | 45 | Used for the age factor (linear decline 1.0 @ 20 → 0.4 @ 70+). |
| target | integer IU | 1000 | Daily target dose. |
| date | ISO 8601 or ms | now | Override the moment of calculation. |
| day | boolean | true | Include day[] (15-min samples through the local day). |
| year | boolean | true | Include year[] (monthly solar-noon samples with cloud climatology). |
| dayStep | integer 5–60 | 15 | Day-curve resolution in minutes. |
{
"input": { "lat", "lon", "date" (ISO), "skinType", "areaFraction", "age", "targetIU" },
"sun": { "elevation", "zenith", "declination" } // degrees
"uvi": { "value", "clearSky", "source": "live" | "clear-sky", "deltaMin" },
"synthesis": {
"iuPerMin",
"minutesToTarget", // null if synthesis is closed
"medMinutes", // minutes to 1 MED (sunburn threshold)
"elevationGate", // 0..1, low-sun penalty
"ageFactor", // 0.4..1.0
"viable" // boolean
},
"peak": { "solarNoon" (ISO), "minutesToTargetAtNoon", "medMinutesAtNoon" },
"window": { "start" (ISO|null), "end" (ISO|null) },
"day": [ { "hour", "time", "elev", "uvi", "uviSource",
"iuPerMin", "minutesToTarget" }, ... ],
"year": [ { "month" (1..12), "solarNoon", "elev", "uviClear",
"cloudPct", "minutesClear", "minutesCloudy" }, ... ]
}
Times in window and peak.solarNoon are ISO UTC. day[].time is also ISO UTC; the hour field is a local-day fraction (0–24).
| Status | When |
|---|---|
| 400 | Missing or invalid lat/lon, malformed date, or out-of-range values. |
| 500 | Internal failure. Body includes a detail string for debugging. |
Error bodies are JSON: { "error": "...", "detail"?: "..." }. When the upstream UV index feed is unreachable, the response still succeeds with uvi.source = "clear-sky" — no error is raised, but expect lower accuracy under heavy cloud cover.
For Claude Desktop, Claude Code, and other MCP clients, D Fetcher also speaks the Model Context Protocol over Streamable HTTP.
https://dfetcher.com/mcp
Exposes three tools — calculate, geocode, and describe_skin_types. Example Claude Desktop config:
{
"mcpServers": {
"dfetcher": { "type": "http", "url": "https://dfetcher.com/mcp" }
}
}