REST is a way to expose and consume services over plain HTTP. Once you can read a URL, pick the right method, and interpret a status code, you can work with most web APIs. This article is meant for developers building or consuming web services, and who should have basic familiarity with HTTP and JSON.
What REST is
REST (Representational State Transfer) is an architectural style, not a protocol or a product. A REST API models the things it manages as resources (an order, a customer, a shipment). Each resource has a URL, and you act on it using standard HTTP methods.
Three ideas explain most of how REST behaves in practice:
-
Resources have addresses.
/orders/1042identifies one order. The same URL always refers to the same thing. -
The method says what to do. The URL says what; the HTTP method (
GET,POST, and so on) says how. You don't invent verbs like/getOrderor/deleteOrder. - Requests are stateless. Each request carries everything the server needs, including credentials. The server doesn't remember your previous call, which is why REST services scale and cache well.
The consumer (client) sends a request and the provider (server) returns a response. The data format is up to the service. JSON is by far the most common today, but XML, CSV, and plain text are all valid. REST doesn't require XML or any particular envelope, which is the main practical difference from SOAP.
Anatomy of a request
Every REST call starts with a URL. Here is a typical one, broken into its parts:
-
Scheme: use
https. Plainhttpsends credentials and data in clear text and is only appropriate for local development. - Host and port: where the service lives. The port is omitted when it's the default (443 for HTTPS).
-
Path: identifies the resource.
/v1/ordersis the collection;/v1/orders/1042is one member of it. Thev1is a version, a common convention that lets the provider change the API without breaking existing consumers. -
Query string: optional
key=valuepairs, separated by&, used to filter, sort, or paginate. Values must be URL-encoded (a space becomes%20).
Rule of thumb: put identity in the path (/orders/1042) and refinement in the query string (?status=shipped). Never put secrets such as passwords or API keys in a URL, because URLs end up in logs, browser history, and proxies.
Older services often expose a single endpoint with parameters, for example http://localhost:8080/OrderStatus?ParmA=1&Parm2=B. That still works as an HTTP call, but it's closer to a remote procedure than to resource-oriented REST. You'll see both styles in practice.
HTTP methods
The method tells the server what you want to do with the resource.
| Method | Use it to | Safe* | Idempotent** | Typical success |
|---|---|---|---|---|
GET |
Read a resource or a collection | Yes | Yes | 200 |
POST |
Create a resource, or trigger an action | No | No | 201 or 202 |
PUT |
Replace a resource with the body you send | No | Yes | 200 or 204 |
PATCH |
Change part of a resource | No | Not guaranteed | 200 or 204 |
DELETE |
Remove a resource | No | Yes | 204 |
* Safe: doesn't change anything on the server. ** Idempotent: sending the same request several times has the same effect as sending it once.
Idempotency matters when a call times out and you don't know whether it succeeded. Retrying a GET, PUT, or DELETE is safe. Retrying a POST can create a duplicate order, so many APIs accept an Idempotency-Key header to let you retry safely. Check your provider's documentation.
PUT and PATCH are often confused. PUT replaces the whole resource, so fields you leave out may be cleared. PATCH sends only the fields that change. Some APIs support only one of them, and some use PUT for partial updates anyway, so confirm the behavior before relying on it.
Status codes
Every response begins with a three-digit status code. The first digit tells you the category; the rest give detail.
| Range | Meaning | Codes you'll see most |
|---|---|---|
| 1xx | Informational | Rarely handled directly |
| 2xx | Success |
200 OK, 201 Created, 202 Accepted, 204 No Content
|
| 3xx | Redirection |
301 Moved Permanently, 304 Not Modified
|
| 4xx | Client error: fix the request |
400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 422 Unprocessable Content, 429 Too Many Requests
|
| 5xx | Server error: the provider failed |
500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout
|
A few distinctions trip people up:
-
401 vs. 403:
401means the server doesn't know who you are (missing or invalid credentials).403means it knows who you are and you're not allowed. -
400 vs. 422:
400usually means the request is malformed (bad JSON).422means it parsed but failed validation (for example, a negative quantity). -
202 Accepted: the request was received but isn't finished. Look for a status URL in the response, often in the
Locationheader.
Headers
Headers are name/value pairs that carry metadata about the request or response. They don't change what you're asking for; they describe the context. These are the ones you'll use most:
| Header | Direction | Purpose |
|---|---|---|
Authorization |
Request | Credentials, such as Bearer <token> or Basic <base64>. |
Content-Type |
Both | Format of the body being sent, for example application/json. |
Accept |
Request | Format you want back. |
Location |
Response | URL of a newly created resource (with 201) or a redirect target. |
ETag / If-Match
|
Response / Request | Version tag for a resource. Send it back with If-Match to avoid overwriting someone else's changes. |
Cache-Control |
Both | Caching rules. |
Retry-After |
Response | How long to wait before retrying, typically with 429 or 503. |
Header names are case-insensitive. Services can also define custom headers, conventionally prefixed with X- in older APIs.
Authentication: Because REST has no built-in security layer, authentication is usually done with a header. Prefer token-based schemes such as OAuth 2.0 bearer tokens or API keys. If you use Basic authentication, only do so over HTTPS, since the credentials are encoded, not encrypted.
Request and response bodies
GET and DELETE requests normally have no body. POST, PUT, and PATCH do, and the Content-Type header must match what you send. A JSON body looks like this:
{
"customerId": "C-381",
"items": [
{ "sku": "WID-100", "qty": 12 }
]
}
A common mistake is sending JSON without Content-Type: application/json. The server may then reject the request with 415 Unsupported Media Type or silently ignore the body.
End-to-end example
This example creates an order, then reads it back. The host and fields are illustrative; substitute your provider's.
1. Create the order
curl
curl -i -X POST https://api.example.com/v1/orders \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"customerId":"C-381","items":[{"sku":"WID-100","qty":12}]}'
Response
HTTP/1.1 201 Created
Location: /v1/orders/1042
Content-Type: application/json
{ "id": 1042, "status": "pending", "customerId": "C-381" }
The 201 tells you the order exists, and Location tells you where to find it.
2. Read it back
JavaScript (fetch)
const res = await fetch("https://api.example.com/v1/orders/1042", {
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/json",
},
});
if (!res.ok) {
throw new Error(`Request failed: ${res.status} ${res.statusText}`);
}
const order = await res.json();
console.log(order.status); // "pending"
Python (requests)
import requests
res = requests.get(
"https://api.example.com/v1/orders/1042",
headers={"Authorization": f"Bearer {token}", "Accept": "application/json"},
timeout=10,
)
res.raise_for_status() # raises on 4xx/5xx
order = res.json()
print(order["status"])
Note the res.ok check in JavaScript. fetch only rejects its promise on network failure, so a 404 or 500 still "succeeds" unless you check the status yourself. In Python, always set a timeout; requests waits indefinitely by default.
3. Filter a collection
GET /v1/orders?status=shipped&limit=25&sort=-createdAt
Collection endpoints usually support filtering, sorting, and pagination through query parameters. The names vary by provider, so check the API reference.
Handling errors
Good error handling in a REST client comes down to deciding, per status code, whether to fix, retry, or give up:
| Situation | What to do |
|---|---|
| 400, 404, 422 | Don't retry. The request itself is wrong. Log the response body, which often explains why. |
| 401 | Refresh or re-acquire the token once, then retry. If it still fails, stop. |
| 403 | Don't retry. Permissions need to change. |
| 409 | Re-read the resource, reconcile the conflict, and try again. |
| 429 | Wait for the time in Retry-After, then retry. |
| 500, 502, 503, 504, timeouts | Retry with exponential backoff and a cap. Only retry non-idempotent calls (POST) if the API supports idempotency keys. |
Many APIs return a structured error body. Parse and log it rather than only the status code:
{
"error": {
"code": "invalid_quantity",
"message": "qty must be greater than 0",
"field": "items[0].qty"
}
}
REST vs. SOAP
REST is usually the lighter option, but it isn't always the right one. The table summarizes the trade-offs; see SOAP Basics for the other side.
| REST | SOAP | |
|---|---|---|
| Format | Any; usually JSON | XML envelope |
| Contract | Optional (often OpenAPI) | WSDL, strictly defined |
| Transport | HTTP | HTTP and others |
| Security | Relies on HTTPS and token-based auth such as OAuth 2.0 | WS-Security built into the standard |
| Learning curve | Low. Testable with a browser or curl | Higher. Needs tooling |
Strengths of REST: lightweight, human-readable, cacheable, easy to build, test, and maintain.
Limitations to plan for:
- No single built-in security standard. You assemble it from HTTPS and an auth scheme, so the quality depends on the provider's implementation.
- Tied to HTTP semantics. That's ideal for request/response APIs, but awkward for long-running or event-driven work, which usually needs
202 Acceptedplus polling, or webhooks. - No enforced contract. Without a published spec, you rely on documentation staying accurate.
REST in CIC Studio
In CIC Studio, you call REST services with the Web Service Consumer object. The concepts above map directly onto it: the URL, method, and headers you've just read about are what you configure, and the HTTP header information is used in the object's related Request and Response actions.
To go further:
Troubleshooting checklist
When a call doesn't behave, work through these in order:
- Reproduce it with curl. If it works there but not in your code, the problem is in how your client builds the request.
- Check the status code first, then the response body. The body usually names the problem.
- Verify the URL. Look for a missing version segment, a trailing slash the server cares about, or unencoded characters in the query string.
-
Verify the method.
405 Method Not Allowedmeans the endpoint exists but not for that method. -
Verify headers. Confirm
Authorizationis present and unexpired, and thatContent-Typematches the body. - Validate the body as well-formed JSON and compare field names and types to the API reference.
- Check for timeouts and rate limits if failures are intermittent.
Frequently Asked Questions
-
Is REST a protocol?
No. REST is a set of design constraints. HTTP is the protocol, and a "RESTful" API is one that uses HTTP in line with those constraints.
-
Do I have to use JSON?
No, but you almost always will. The provider decides the supported formats, and theContent-TypeandAcceptheaders tell each side which one is in use.
-
Should I use PUT or POST to create something?
UsePOSTwhen the server assigns the identifier (POST /orders). UsePUTwhen you choose the identifier yourself and want the call to be safely repeatable (PUT /orders/1042).
-
Why did I get a 200 with an error in the body?
Some APIs don't follow HTTP semantics strictly and return200with an error payload. Read the provider's documentation and check the body as well as the status.
Comments
0 comments
Please sign in to leave a comment.