WeatherAPI returns a numeric condition.code in every current, forecast, and history response. That number is stable — it doesn’t change based on request parameters, language headers, or anything else. What changes is the condition.text string sitting next to it. That distinction matters more than most integrations treat it.
The short version: if you’re storing condition.text in a database or caching it as a display string, you’re building on sand. The text is English, it’s not guaranteed to be consistent across API versions, and it’s not designed to be your display layer. The condition.code is the stable contract. Build your locale handling around that.
What the Condition Code Actually Is
WeatherAPI’s condition codes are a flat integer namespace — codes like 1000 (Clear/Sunny), 1063 (Patchy rain nearby), 1183 (Light rain), and so on up through the heavy snow and blizzard end of the spectrum. There are around 50 distinct codes. We map raw METAR observations and model-derived weather states to these codes ourselves rather than passing through someone else’s classification scheme, which means the mapping reflects decisions we made about how to bucket ambiguous states — particularly for mixed precipitation and night-time conditions.
The condition.text and condition.icon fields alongside the code are convenience fields. Useful for quick prototypes. For any production multi-language app, they’re a trap.
The Failure Mode
Say you’re caching API responses in Redis with a 15-minute TTL — sensible, nothing wrong with that. But you’re caching the whole parsed response object, including condition.text. A user in Berlin hits your app and gets “Patchy rain nearby” because that’s what came back from the API and what you stored. You’re now serving English strings to a German-language UI, and your cache is the reason.
The sneakier version: you store condition.text to a database as a label field. Six months later you want to add French support. Now you have a database full of English strings with no foreign key back to anything you can translate from. The code was right there the whole time.
The Fix: Treat condition.code as Your Only Stable Identifier
Keep your own condition map — a simple JSON file or a small database table is enough. Keyed by condition code, with one entry per locale you support:
{
"1000": {
"en": "Clear",
"de": "Klar",
"fr": "Clair",
"es": "Despejado"
},
"1063": {
"en": "Patchy rain nearby",
"de": "Vereinzelte Regenschauer in der Nähe",
"fr": "Pluie éparse à proximité",
"es": "Lluvia irregular cercana"
}
}
Your application receives the API response, extracts condition.code, looks it up in your map for the user’s locale, and uses that as the display string. The condition.text from the API never touches your storage or your UI layer.
WeatherAPI publishes the full condition code list. It’s static enough that a one-time build of this map is worth doing — you’re not polling it, not regenerating it per request. It’s a build artifact.
Day vs. Night Condition Codes Are the Same Code
This trips people up more than you’d expect. The condition.code doesn’t change between day and night variants of the same weather state. What changes is the condition.icon URL — day and night icon variants exist, and the API picks the right one based on local time. The numeric code is identical whether it’s noon or midnight under a clear sky.
If your UI uses different display strings for day vs. night (“Sunny” during the day, “Clear” at night for the same underlying state), drive that off the is_day field in the response, not off any variation in the condition code. Your locale map handles this with a small structure change:
{
"1000": {
"en": { "day": "Sunny", "night": "Clear" },
"de": { "day": "Sonnig", "night": "Klar" }
}
}
Then your lookup becomes conditionMap[code][locale][isDay ? 'day' : 'night']. A little more verbose, but now your UI actually says “Sonnig” and “Klar” for the same code depending on context, which is what a German speaker expects.
Handling the Icon Side of Things
The API returns a CDN URL in condition.icon. That works fine for quick builds. But if you want to self-host icons, control caching headers, or swap in a custom icon set that matches your design system, you need off that CDN URL dependency.
The icon filenames in WeatherAPI’s CDN are predictably structured — they encode the condition code and the day/night variant. Once you know the pattern, you can build your own icon URL generation from condition.code and is_day locally, without touching the API’s icon URL at all. Combine that with your own hosted icon set and you’ve removed a runtime external dependency from your UI render path.
That said, self-hosting icons is only worth the effort if you’re doing significant volume or have specific design requirements. For most applications, the CDN URL is fine.
Severity Bucketing by Code
Your locale map is also a natural place to store severity metadata. Condition code 1000 (clear) and 1087 (thundery outbreaks nearby) are very different operationally, even if your UI’s job is just to display them. If you’re building any alerting, scheduling, or outdoor-activity feature on top of conditions, attach a severity tier to each code in your map.
A simple integer from 0–3 alongside each code — 0 for clear, 1 for light precipitation, 2 for moderate, 3 for severe — lets you write application logic against the tier rather than a growing list of specific codes. That’s cleaner than conditionals like if (code === 1087 || code === 1273 || code === 1276) scattered through your codebase.
The exact tier definitions are a judgment call, and we don’t publish an authoritative mapping from condition codes to a severity scale because what counts as “severe” is domain-dependent. Code 1225 (heavy blizzard) is severe for road logistics; for a ski resort it might just mean a busy weekend. Own that decision explicitly in your map rather than treating it as something derivable purely from the API.
Keeping the Map Current
The condition code list doesn’t change frequently, but if you’re building something long-lived, add a validation step to your build or test suite that checks your local map against the API’s known code list. A new code appearing in a live response that isn’t in your map will silently display nothing — or whatever your fallback is — which is a bad user experience to debug months after launch when a new weather state starts getting reported in your users’ region.
The fix is simple: log a warning (or throw in test environments) whenever a condition code appears in an API response that doesn’t exist in your local map. Don’t swallow it silently.
What This Looks Like End-to-End
Rough pseudocode for the full pattern:
// At startup / build time
const conditionMap = loadJSON('./conditions.json')
// Per API response
function getDisplayCondition(apiResponse, locale, fallbackLocale = 'en') {
const code = apiResponse.current.condition.code
const isDay = apiResponse.current.is_day === 1
const timeOfDay = isDay ? 'day' : 'night'
const entry = conditionMap[code]
if (!entry) {
console.warn(`Unknown condition code: ${code}`)
return { text: apiResponse.current.condition.text, severity: -1 }
}
const localeEntry = entry[locale] ?? entry[fallbackLocale]
return {
text: localeEntry[timeOfDay] ?? localeEntry['day'],
severity: entry.severity
}
}
The API response goes in, a locale-correct display object comes out. The condition code is the only thing bridging those two sides. Nothing stored from condition.text, nothing locale-sensitive leaking into your cache.
One real caveat here: this pattern assumes your user’s locale is known at render time. If you’re server-side rendering and caching the rendered output rather than the raw API response, you need to either cache per-locale or not cache the rendered condition string at all. Caching a pre-rendered German string and serving it to a French user is the same problem as the English one, just shifted a layer up the stack.
If you’re building a multi-language product, it’s worth auditing what you’re actually storing and displaying from condition.text today. It’s a 30-minute fix now or a migration later.
