Error codes and what to do about each
Every error is JSON with a stable error code and a readable message, plus field, reason or upstream where they help. Branch on error, not on message, which may be reworded.
| HTTP | error | When | Counts against quota | What to do |
|---|---|---|---|---|
| 401 | invalid_key | The key is missing, wrong, revoked, or its plan has ended. The answer is the same in every case, so keys cannot be probed. | No (not metered) | Check the Authorization header is Bearer fza_.... Create a new key on the account page if needed. |
| 422 | invalid_request | No input, both inputs, a latitude or longitude out of range or not a number, or an address shorter than 5 or longer than 300 characters. field names the parameter. | No (refunded) | Fix the parameter named in field. Do not retry unchanged. |
| 404 | address_not_found | No geocoder matched the address (reason: "no_match"), or only a street or place matched, which is not precise enough for a flood zone (reason: "not_address_level"). | Yes | Include the house number, city and state, or geocode it yourself and send lat and lon. |
| 429 | quota_exceeded | The key's plan has used its calls for this calendar month (UTC). | Not applicable | Upgrade on the account page, or wait for the first of next month (UTC). |
| 429 | rate_limited | More than 5 requests in one second on one key. | No (not metered) | Wait one second and retry. Spread batch jobs out, or run them at a steady rate. |
| 503 | upstream_unavailable | FEMA's map service (upstream: "fema_nfhl") or the address geocoder (upstream: "geocoder") failed or timed out. Sent with a Retry-After header. | No (refunded) | Retry after the number of seconds in Retry-After (30), with backoff if it repeats. |
| 500 | internal_error | Something failed on our side. | No (refunded) | Retry later; if it repeats, email [email protected] with the time and the query. |
Retrying well
- Retry only 503 and 429
rate_limited. A 404 or 422 will fail the same way again. - Wait at least the
Retry-Afterseconds, then back off (for example 30, 60, 120 seconds) if FEMA stays down. - A
200with"mapped": falseis an answer, not an error. Retrying it gets a fresh FEMA query (unmapped answers are never cached), which is worth one retry if you expected a zone.
Example bodies
{
"error": "invalid_key",
"message": "Missing or invalid API key."
}{
"error": "invalid_request",
"message": "lat must be a number between -90 and 90 (WGS84 decimal degrees).",
"field": "lat"
}{
"error": "address_not_found",
"message": "We could only match a street or place, not a specific address, and a street or town center is not a flood zone point. Include the house number, city and state, or send lat and lon.",
"reason": "not_address_level"
}{
"error": "quota_exceeded",
"message": "This key's plan allows 100 calls per calendar month (UTC). Upgrade or wait for the next month."
}{
"error": "rate_limited",
"message": "More than 5 requests per second on this key. Wait a second and retry; this call was not counted."
}{
"error": "upstream_unavailable",
"message": "FEMA's flood map service did not answer. This call was not counted against your quota; retry shortly.",
"upstream": "fema_nfhl",
"retry_after": 30
}{
"error": "internal_error",
"message": "Something went wrong on our side. This call was not counted against your quota."
}