> ## Content Index
> Fetch the complete content index at: https://www.pinglabz.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# REST API Response Codes and Payloads: What 200, 404, and 500 Tell You
- URL: https://www.pinglabz.com/rest-api-response-codes-network/
- Published: 2026-07-12T02:58:38.000Z
- Updated: 2026-08-24T09:42:27.000Z
- Description: A script that ignores HTTP response codes eventually lies about success. This covers the codes that matter for RESTCONF, Catalyst Center, and SD-WAN Manager APIs, what 200, 404, and 500 tell you, and how to act on each.
- Author: Jaime
- Tags: Network Automation, #Import 2026-08-01 19:54

An automation script that ignores HTTP response codes is a script that will eventually break something quietly. It sends a change, gets back a 409 it never checks, reports success, and moves on, and now your intent and the device's reality have diverged and nobody knows. Reading and handling response codes is not pedantry; it is the difference between automation you can trust and automation that occasionally lies to you.

This article covers the HTTP response codes that matter for network APIs (RESTCONF, Catalyst Center, SD-WAN Manager) and how to act on each. It extends the [Network Automation cluster guide](https://www.pinglabz.com/network-automation/).

## The Five Classes, in One Line Each

HTTP status codes group into five ranges by their first digit, and knowing the range tells you most of what you need:

2xx Success

It worked. 200 (here is the data), 201 (created), 204 (done, no content to return).

3xx Redirect

Go somewhere else. Rare in network APIs; your client library usually follows these automatically.

4xx Client error

**You** did something wrong. Bad request, bad auth, wrong path. Retrying unchanged will not help.

5xx Server error

**The device** did something wrong or could not cope. Sometimes worth a retry with backoff.

The single most useful reflex: **4xx is your fault, 5xx is theirs.** A 4xx means fix your request; a 5xx means the device failed and a careful retry might succeed. Confusing the two leads to either pointless retry loops (retrying a 400 forever) or giving up too early (not retrying a transient 503).

## The Codes You Will Actually See

200 OKA GET succeeded and the body has your data. The everyday success for reads.

201 CreatedA POST created a new resource. You made something that did not exist before.

204 No ContentA PATCH/PUT/DELETE succeeded, and there is nothing to return. **This is success**, and it trips people up because the body is empty.

400 Bad RequestMalformed request: bad JSON/XML, a value that fails the YANG model's constraints. Fix the payload.

401 UnauthorizedMissing, wrong, or expired credentials/token. Re-authenticate; do not retry with the same bad credential.

403 ForbiddenAuthenticated but not permitted. A least-privilege boundary, or your role lacks the right. Not a retry.

404 Not FoundThe path/resource does not exist. Usually a wrong YANG path or a typo'd interface name.

409 ConflictThe request conflicts with current state (e.g. creating something that already exists, or a datastore lock). Read the current state and reconcile.

429 Too Many RequestsRate-limited. Back off (honour `Retry-After` if present). Ignoring it looks like an attack.

500 / 503Server error / unavailable. The device broke or is overloaded. Retry with exponential backoff; if it persists, it is a device problem.

The two that catch people out most: **204** (an empty body is success, not failure, on a config change) and **429** (a controller like Catalyst Center will rate-limit you, and a script that hammers through 429s can get its token throttled or revoked).

## Handling Them in Code

The pattern is not complicated, and it is the difference between trustworthy and reckless automation:

```
r = requests.patch(url, data=payload, headers=hdr, auth=auth, verify=True)

if r.status_code == 204:
    log("change applied")
elif r.status_code == 401:
    reauthenticate()          # token expired; get a new one, retry once
elif r.status_code == 403:
    fail("not authorized for this operation")   # do not retry
elif r.status_code == 409:
    reconcile(get_current_state())              # conflict; read and merge
elif r.status_code == 429:
    backoff(r.headers.get("Retry-After", 30))   # rate-limited; wait
elif 500 <= r.status_code < 600:
    retry_with_backoff()      # device error; a careful retry may work
else:
    fail(f"unexpected {r.status_code}: {r.text}")
```

The rules encoded there are the important part. **Never blindly retry a 4xx** (except 401 after re-authenticating and 429 after backing off); the request is wrong and retrying it unchanged just wastes calls. **Do back off on 429 and 5xx.** And **always check**, because the failure mode of not checking is silent divergence, which is the worst kind.

## Read the Body, Too

The status code tells you the category; the response body often tells you exactly what went wrong. RESTCONF returns a structured error in the body of a 4xx:

```
{
  "errors": {
    "error": [{
      "error-type": "application",
      "error-tag": "invalid-value",
      "error-message": "invalid value for: mtu"
    }]
  }
}
```

That `error-message` is the difference between "the change failed" and "the change failed because the MTU value was invalid." A robust script logs the body on any error, not just the code. When you are debugging why a PATCH returns 400, the body is where the actual reason lives.

## A Word on Idempotency

The HTTP methods differ in whether repeating them is safe, which matters when a retry might send the same request twice:

- **GET, PUT, DELETE, PATCH** are idempotent: doing them twice has the same effect as doing them once. Safe to retry.
- **POST** is not idempotent: two POSTs can create two resources. Retrying a POST after an ambiguous failure risks a duplicate. Design for it (check whether the resource was created before retrying).

This is why config changes in RESTCONF usually use PUT or PATCH rather than POST: a retried PUT converges to the intended state, while a retried POST might make a mess. When your automation must be resilient to transient failures, prefer the idempotent methods.

## FAQ

### My PATCH returned 204 with an empty body. Did it fail?

No. 204 is success with no content to return, the normal result of a successful config change. The empty body is expected.

### Should I retry a 400?

No. A 400 means your request is malformed. Retrying it unchanged will fail identically. Fix the payload (the response body usually says what is wrong).

### What is the difference between 401 and 403?

401 = not authenticated (bad/expired/missing credentials, so re-authenticate). 403 = authenticated but not authorized (a permission boundary, so do not retry, fix the role).

### How should I handle 429?

Back off and retry, honouring the `Retry-After` header if the API provides it. Never hammer through 429s; a controller can throttle or revoke your token.

### Which methods are safe to retry?

GET, PUT, DELETE, PATCH are idempotent and safe. POST is not, so retrying it can create duplicates. Prefer PUT/PATCH for config so retries converge cleanly.

## Key Takeaways

- **Always check the status code.** Not checking means silent divergence between intent and reality, the worst automation failure.
- **4xx is your fault, 5xx is theirs.** Fix a 4xx request; a 5xx may succeed on a careful retry.
- The tricky successes: **204** (empty body is success on a change) and knowing **201** means created.
- The tricky errors: **401** (re-auth) vs **403** (permission, do not retry), **409** (conflict, reconcile), **429** (rate-limit, back off).
- **Read the response body** on errors; RESTCONF's `error-message` tells you exactly what failed.
- Prefer **idempotent methods** (PUT/PATCH) for config so retries converge instead of duplicating.

Next: [RESTCONF on Cisco IOS XE](https://www.pinglabz.com/restconf-cisco-ios-xe/) where these codes appear live, or the [Network Automation cluster guide](https://www.pinglabz.com/network-automation/).