# Best Practices

Follow these recommendations to build reliable, performant integrations with the AccuWeather Enterprise API.


## Performance

### Use GZIP compression

All API responses support GZIP compression, which typically reduces payload size by 80% or more. Most modern HTTP clients enable this by default — ensure your client is not explicitly disabling it.

Add the `Accept-Encoding` header to your requests:

<CodeTabs syncKey="language">
```bash
curl -H "Accept-Encoding: gzip,deflate" \
  "https://api.accuweather.com/locations/v1/search?q=san&apikey={your key}"
```

```typescript
const response = await fetch(
  "https://api.accuweather.com/locations/v1/search?q=san&apikey={your key}",
  { headers: { "Accept-Encoding": "gzip" } },
);
const data = await response.json();
```

```javascript
const response = await fetch(
  "https://api.accuweather.com/locations/v1/search?q=san&apikey={your key}",
  { headers: { "Accept-Encoding": "gzip" } },
);
const data = await response.json();
```

```python
import requests

response = requests.get(
    "https://api.accuweather.com/locations/v1/search",
    params={"q": "san", "apikey": "{your key}"},
    headers={"Accept-Encoding": "gzip"}
)
data = response.json()
```

```csharp
using var client = new HttpClient();
client.DefaultRequestHeaders.AcceptEncoding.Add(
    new System.Net.Http.Headers.StringWithQualityHeaderValue("gzip"));

var response = await client.GetAsync(
    "https://api.accuweather.com/locations/v1/search?q=san&apikey={your key}");
var data = await response.Content.ReadAsStringAsync();
```

```java
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create(
        "https://api.accuweather.com/locations/v1/search?q=san&apikey={your key}"))
    .header("Accept-Encoding", "gzip")
    .build();

HttpResponse<String> response =
    client.send(request, HttpResponse.BodyHandlers.ofString());
```
</CodeTabs>

### Use connection pooling

If you are making frequent API calls, reuse HTTP connections rather than opening a new connection per request. Most HTTP client libraries support keep-alive and connection pooling out of the box.

---

## Caching

### Respect cache headers

Every API response includes cache headers that indicate when the data will next be updated. Use these headers to avoid unnecessary requests.

```
Cache-Control: public, max-age=900
Expires: Wed, 02 Apr 2026 15:30:00 GMT
```

- **`Cache-Control: max-age`** — the number of seconds the response can be cached
- **`Expires`** — the absolute time after which the response should be refreshed

Do not request new data until the cache has expired. This reduces unnecessary load and ensures your application always uses the freshest available data.

{/* ### Recommended Cache Durations

Use the cache headers from each response as the source of truth. The following are general guidelines for how frequently data is updated:

| Endpoint | Typical Update Frequency |
| --- | --- |
| Locations | 24 hours |
| Current Conditions | 15–30 minutes |
| Hourly Forecasts | 1 hour |
| Daily Forecasts | 4–6 hours |
| Alerts | 5–15 minutes |
| MinuteCast™ | 5 minutes |
| Air Quality | 1 hour |
| Maps / Imagery | 10–30 minutes |
*/}

### Randomize refresh times

If you are refreshing data across many devices or services, add randomization to prevent all clients from requesting updates at the same clock time (e.g., exactly on the hour). Stagger requests by adding a random offset of a few seconds or minutes to each client's refresh interval.

### Cache MinuteCast™ errors

MinuteCast™ is not available in all regions. For unsupported locations, the API returns an HTTP 400 response. Cache these responses for **72 hours** to avoid repeated requests for locations where MinuteCast™ data is not available.

---

## Error handling

### Distinguish client and server errors
Handle 4xx and 5xx errors differently:

- **4xx errors** (Bad Request, Forbidden, Not Found, Conflict) indicate a problem with the request itself. Do not retry these without fixing the underlying issue (e.g., invalid API key, bad parameters, exceeded quota).
- **5xx errors** (Internal Error, Service Unavailable) indicate a temporary server-side issue. Retry these with exponential backoff.

### Retry with exponential backoff

For transient failures (5xx responses, network timeouts), retry with increasing delays:

<CodeTabs syncKey="language">
```bash
#!/bin/bash
URL="https://api.accuweather.com/currentconditions/v1/347625?apikey={your key}"
for i in 1 2 4; do
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$URL")
  [ "$STATUS" -lt 500 ] && break
  sleep $i
done
```

```typescript
async function fetchWithRetry(
  url: string,
  maxRetries: number = 3,
): Promise<Response> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url);
    if (response.status < 500) return response;
    await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
  }
  throw new Error("Max retries exceeded");
}
```

```javascript
async function fetchWithRetry(url, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url);
    if (response.status < 500) return response;
    await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
  }
  throw new Error("Max retries exceeded");
}
```

```python
import time
import requests

def fetch_with_retry(url, params, max_retries=3):
    for attempt in range(max_retries):
        response = requests.get(url, params=params)
        if response.status_code < 500:
            return response
        time.sleep(2 ** attempt)  # 1s, 2s, 4s
    return response
```

```csharp
async Task<HttpResponseMessage> FetchWithRetryAsync(
    HttpClient client, string url, int maxRetries = 3)
{
    HttpResponseMessage response = null;
    for (int attempt = 0; attempt < maxRetries; attempt++)
    {
        response = await client.GetAsync(url);
        if ((int)response.StatusCode < 500) return response;
        await Task.Delay((int)Math.Pow(2, attempt) * 1000);
    }
    return response;
}
```

```java
HttpResponse<String> fetchWithRetry(HttpClient client, String url, int maxRetries)
        throws Exception {
    HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).build();
    HttpResponse<String> response = null;
    for (int attempt = 0; attempt < maxRetries; attempt++) {
        response = client.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() < 500) return response;
        Thread.sleep((long) Math.pow(2, attempt) * 1000);
    }
    return response;
}
```
</CodeTabs>

---

## Time zones and daylight saving

If you use the `GMTOffset` from the Locations API to calculate local times, you **must** observe the `NextOffsetChange` property. The offset will change at the date and time specified when a location transitions into or out of Daylight Saving Time.

Respecting cache headers (as described above) ensures you always have the most current offset for a location.

---

## Monitoring

Stay informed on service availability for AccuWeather products and services at [status.accuweather.com](https://status.accuweather.com). Subscribe to updates to receive notifications about planned maintenance and incidents.
