HTTP Status Codes & Headers – Understanding API Communication
Over the past few days, I've learned how JavaScript communicates with APIs using the Fetch API.
Today, I went deeper into what actually comes back from an HTTP request.
I explored HTTP status codes, request headers, response headers, and response properties.
These concepts are important because an API response isn't just about the data—it also tells us whether the request succeeded, failed, was redirected, or needs further action.
Let's dive in.
What is an HTTP Status Code?
An HTTP status code is a number returned by a server that tells us what happened with a request.
For example:
200
usually means the request was successful.
Status codes are divided into categories.
| Range | Meaning |
|---|---|
1xx |
Informational |
2xx |
Success |
3xx |
Redirection |
4xx |
Client Error |
5xx |
Server Error |
2xx – Success
These status codes generally indicate that the request was successfully processed.
200 – OK
The request was successful.
200 OK
Commonly returned for successful GET requests.
201 – Created
The server successfully created a new resource.
This is commonly associated with successful POST requests.
201 Created
204 – No Content
The request succeeded, but the server doesn't return a response body.
204 No Content
This is commonly seen with operations where there's nothing to return.
3xx – Redirection
These status codes indicate that further action may be needed to complete the request.
Examples include:
301
302
304
For example:
301 – Moved Permanently
The requested resource has permanently moved to another URL.
304 – Not Modified
The cached version can be used because the resource hasn't changed.
4xx – Client Errors
These generally indicate that something is wrong with the request or the client's access.
400 – Bad Request
The server couldn't understand or process the request.
400 Bad Request
For example, the request might contain invalid data.
401 – Unauthorized
Authentication is required or the provided authentication credentials are not valid.
401 Unauthorized
403 – Forbidden
The server understood the request but refuses to allow it.
403 Forbidden
404 – Not Found
The requested resource couldn't be found.
404 Not Found
This is one of the most common errors you'll encounter while working with APIs.
5xx – Server Errors
These indicate that something went wrong on the server side.
500 – Internal Server Error
The server encountered an unexpected problem.
500 Internal Server Error
Other examples include:
502 Bad Gateway
503 Service Unavailable
Checking Status With Fetch
When using fetch(), we can access the status code through:
const response = await fetch(
"https://jsonplaceholder.typicode.com/users"
);
console.log(response.status);
We can also check:
console.log(response.ok);
response.ok is true for successful HTTP responses in the 200–299 range.
Why response.ok Matters
One important thing I learned is that fetch() does not automatically reject its Promise just because the server returns a 4xx or 5xx response.
For example:
const response = await fetch(
"https://example.com/not-found"
);
console.log(response.status);
We should explicitly check the response:
if (!response.ok) {
throw new Error(
`Request failed: ${response.status}`
);
}
This makes error handling much more reliable.
What are HTTP Headers?
Headers contain additional information about an HTTP request or response.
Think of them as metadata describing the communication.
There are two major types:
Request headers
Response headers
Request Headers
Request headers are sent from the client to the server.
For example:
const response = await fetch(
"https://example.com",
{
headers: {
"Content-Type": "application/json"
}
}
);
The server can use this information to understand how the request should be interpreted.
Content-Type
One of the most common headers is:
Content-Type
For JSON requests:
headers: {
"Content-Type": "application/json"
}
This tells the server that the request body contains JSON.
Authorization Header
APIs often require authentication.
A request might contain an authorization header such as:
headers: {
"Authorization": "Bearer YOUR_TOKEN"
}
The exact authentication mechanism depends on the API.
Private API keys and tokens should not be exposed in frontend code when the API design requires them to remain secret.
Response Headers
Servers also send headers back to the client.
We can access them through:
const response = await fetch(
"https://jsonplaceholder.typicode.com/users"
);
console.log(response.headers);
Getting a Specific Response Header
We can use .get():
console.log(
response.headers.get("content-type")
);
This allows us to inspect specific response metadata.
Response Properties
A Fetch API response contains several useful properties.
For example:
console.log(response.status);
console.log(response.ok);
console.log(response.url);
console.log(response.headers);
These give us information about the response.
Reading Response Data
After checking the response, we can read its body.
For JSON:
const data = await response.json();
console.log(data);
For plain text:
const text = await response.text();
console.log(text);
The appropriate method depends on the response format.
Building Better Error Handling
Now we can combine everything we've learned.
async function getUsers() {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/users"
);
if (!response.ok) {
throw new Error(
`HTTP Error: ${response.status}`
);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error(
"Request failed:",
error.message
);
}
}
getUsers();
Now our application can distinguish between a successful response and an unsuccessful HTTP response.
HTTP Communication Flow
The complete communication process now looks like:
Frontend
│
│ Request
│ Headers + Body
↓
Server / API
│
│ Response
│ Status + Headers + Body
↓
Frontend
The response isn't just data.
It contains:
Status + Headers + Body
Understanding this makes API communication much easier to reason about.
Best Practices
✔ Always check response.ok when handling API requests.
✔ Use appropriate HTTP status codes when building APIs.
✔ Use headers to communicate metadata between client and server.
✔ Don't expose private credentials or secrets in frontend JavaScript.
✔ Handle both network errors and unsuccessful HTTP responses.
✔ Don't assume every successful response contains JSON.
My Biggest Takeaway
Today, I learned that an API response contains much more than just the data I'm trying to retrieve.
The server communicates important information through:
Status Codes + Headers + Response Body
Understanding status codes also makes debugging much easier.
If I see a 404, I know I'm dealing with a missing resource. If I see a 401, I know authentication is involved. If I see a 500, the problem is generally on the server side.
