Network Automation

REST API Response Codes and Payloads: What 200, 404, and 500 Tell You

REST API status code guide: 204 success, 401 vs 403, 409, 429 for network automation
In: Network Automation

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.

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 where these codes appear live, or the Network Automation cluster guide.

Written by
More from Ping Labz
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Ping Labz.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.