Weatherly API v1 · stable

Weatherly API

A small, predictable HTTP API for current conditions, daily forecasts, and place lookup. JSON in, JSON out — no SDK required.

JSON only HTTPS Key-based auth Metric & imperial

Weatherly exposes three resources. Everything is a GET request over HTTPS, and every response body is a JSON object with a top-level data field (or error on failure). Responses are cached at the edge for 60 seconds, so repeated identical calls are cheap.

Demo notice. Weatherly is a fictional product used to illustrate an API reference. All example values below are placeholders, not live measurements.

Quick start

curl
# Replace YOUR_API_KEY with a key from your dashboard
curl "https://api.weatherly.example.com/v1/current?lat=52.37&lon=4.90" \
  -H "Authorization: Bearer YOUR_API_KEY"

Authentication

Every request must include an API key. Pass it in the Authorization header using the Bearer scheme. Keys are scoped to a single project and can be rotated from your dashboard at any time.

Header
Authorization: Bearer wth_live_9f2c8a1d4b7e

A query-string fallback (?key=…) exists for quick browser tests only. Avoid it in production — URLs leak into logs and referrer headers.

Never ship a key in client-side code. Proxy requests through your own server and keep the key in an environment variable such as WEATHERLY_API_KEY.

HeaderValueRequiredNotes
AuthorizationBearer <API_KEY>YesPrimary auth method.
Acceptapplication/jsonNoDefault; other types return 406.
X-Request-IdAny stringNoEchoed back for tracing.

Base URL

https://api.weatherly.example.com/v1

All paths below are relative to this base. The version segment is required; unversioned requests return 404. Timestamps are ISO 8601 in UTC unless a tz parameter is supplied.


Endpoints

Three endpoints cover the common cases: /current, /forecast, and /locations.

GET /v1/current

Returns the latest observed conditions for a coordinate pair. Cached for 60 seconds.

Parameters

NameTypeRequiredDescription
latnumberYesLatitude, −90 to 90.
lonnumberYesLongitude, −180 to 180.
unitsstringNometric (default) or imperial.
tzstringNoIANA time zone, e.g. Europe/Amsterdam.
Request
curl "https://api.weatherly.example.com/v1/current?lat=52.37&lon=4.90&units=metric" \
  -H "Authorization: Bearer YOUR_API_KEY"
Response · 200 OK
{
  "data": {
    "location": { "name": "Amsterdam", "country": "NL", "lat": 52.37, "lon": 4.90 },
    "observed_at": "2025-03-04T09:15:00Z",
    "condition": "partly_cloudy",
    "temperature": { "value": 9.4, "unit": "celsius" },
    "feels_like": { "value": 7.1, "unit": "celsius" },
    "humidity": 78,
    "wind": { "speed": 14.2, "direction": 240, "unit": "kmh" },
    "pressure": 1014
  }
}
GET /v1/forecast

Returns daily forecast entries for up to 14 days ahead, starting today.

Parameters

NameTypeRequiredDescription
latnumberYesLatitude, −90 to 90.
lonnumberYesLongitude, −180 to 180.
daysintegerNo1–14. Default 7.
unitsstringNometric or imperial.
includestringNoComma-separated extras: sun, precip.
Request
curl "https://api.weatherly.example.com/v1/forecast?lat=52.37&lon=4.90&days=3&include=sun,precip" \
  -H "Authorization: Bearer YOUR_API_KEY"
Response · 200 OK
{
  "data": {
    "location": { "name": "Amsterdam", "country": "NL" },
    "units": "metric",
    "days": [
      {
        "date": "2025-03-04",
        "condition": "partly_cloudy",
        "temp_min": 4.2,
        "temp_max": 10.1,
        "precip_chance": 20,
        "sun": { "sunrise": "07:21", "sunset": "18:24" }
      },
      {
        "date": "2025-03-05",
        "condition": "rain",
        "temp_min": 5.0,
        "temp_max": 8.8,
        "precip_chance": 70,
        "sun": { "sunrise": "07:19", "sunset": "18:26" }
      }
    ]
  }
}
GET /v1/locations

Resolves a place name to coordinates. Use the returned lat/lon with the other two endpoints.

Parameters

NameTypeRequiredDescription
qstringYesFree-text query, 2–80 characters.
limitintegerNo1–20 results. Default 5.
countrystringNoISO 3166-1 alpha-2 filter, e.g. NL.
Request
curl "https://api.weatherly.example.com/v1/locations?q=amster&limit=2" \
  -H "Authorization: Bearer YOUR_API_KEY"
Response · 200 OK
{
  "data": {
    "query": "amster",
    "results": [
      {
        "name": "Amsterdam",
        "country": "NL",
        "region": "North Holland",
        "lat": 52.37,
        "lon": 4.90,
        "timezone": "Europe/Amsterdam"
      },
      {
        "name": "Amstelveen",
        "country": "NL",
        "region": "North Holland",
        "lat": 52.31,
        "lon": 4.86,
        "timezone": "Europe/Amsterdam"
      }
    ]
  }
}

Errors

Failures return a non-2xx status and a JSON body with a stable error.code. Branch on the code, not the message — messages may be reworded.

Response · 401 Unauthorized
{
  "error": {
    "code": "invalid_api_key",
    "message": "The API key is missing, malformed, or revoked.",
    "request_id": "req_8c1f4a20"
  }
}
StatusCodeMeaning
400invalid_parameterA parameter is missing, out of range, or the wrong type.
401invalid_api_keyKey missing, malformed, or revoked.
403plan_restrictedEndpoint or parameter not available on your plan.
404not_foundUnknown path or no matching location.
429rate_limitedToo many requests. See Rate limits.
500internal_errorUnexpected server fault. Retry with backoff.

Include the request_id from the response when contacting support — it maps to a server-side trace.


Rate limits

Limits are applied per API key and reset on a rolling 60-second window. Every response carries headers so you can throttle proactively.

HeaderExampleDescription
X-RateLimit-Limit600Requests allowed per minute.
X-RateLimit-Remaining412Requests left in the current window.
X-RateLimit-Reset41Seconds until the window resets.
Retry-After12Present on 429 only.

Plan tiers (illustrative). Free: 60 req/min. Developer: 600 req/min. Team: 6,000 req/min. Exact figures depend on your subscription — check your dashboard for the live numbers.

Handling 429 with exponential backoff
// Retry on 429 or 5xx, backing off up to 5 attempts.
async function getWeather(url, key) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${key}` }
    });
    if (res.ok) return res.json();
    if (res.status !== 429 && res.status < 500) throw new Error(await res.text());

    const wait = Number(res.headers.get("Retry-After") ?? 2 ** attempt);
    await new Promise(r => setTimeout(r, wait * 1000));
  }
  throw new Error("Gave up after 5 attempts");
}

Support

Questions about integration, quotas, or key rotation? Reach the team at [email protected] or call +1 (555) 014-2200 on weekdays, 09:00–17:00 CET.