ScreenshotNeo

BlogComparisons

CRUD vs. REST: What’s the Difference?

CRUD describes data operations; REST describes an architecture for networked systems. Learn how they relate, where HTTP fits, and how to compare APIs.

By the ScreenshotNeo team30 September 20269 min read

CRUD vs. REST: What's the Difference?

CRUD and REST describe different layers of an application. CRUD is the set of operations you perform on data: create, read, update, and delete. REST (Representational State Transfer) is an architectural style for distributed systems. A REST API may expose CRUD operations over HTTP, but CRUD is not REST, and REST is not limited to CRUD.

This distinction matters when you design, document, or review an API. A service can use HTTP verbs and JSON while still lacking REST’s broader constraints, such as statelessness, cacheability, a uniform interface, layered operation, and (at the highest maturity level) hypermedia controls.

CRUD and REST at a glance

Concept What it describes Typical example
CRUD Operations applied to data or a resource Create a user, read a user, update a user, delete a user
REST Constraints for communication between clients and servers A stateless client requests representations of resources through a uniform interface
HTTP mapping A common way to expose CRUD over the web POST, GET, PUT, PATCH, and DELETE

CRUD can live entirely inside a database layer, service class, command handler, or desktop application. REST concerns the boundary between distributed components. You can therefore implement CRUD without a network API, or expose CRUD through an HTTP API that is only loosely REST-oriented.

CRUD names the four common operations performed on a resource.
CRUD names the four common operations performed on a resource.

What CRUD means

CRUD is an application and persistence convention:

  • Create: add a new record or resource.
  • Read: retrieve one record, a collection, or a representation.
  • Update: replace or modify an existing record.
  • Delete: remove a record or make it unavailable.

The names do not prescribe URI shapes, transport protocols, status codes, authentication, caching, or message formats. A CRUD system can use SQL, an in-process repository, a message queue, GraphQL mutations, or HTTP.

What REST means

REST is the architectural style Roy Fielding described in his dissertation. It models interactions around resources and their representations, then applies constraints intended to improve scalability, visibility, and evolvability. Fielding’s constraints include:

  1. Client-server separation: the user interface and data concerns evolve independently.
  2. Statelessness: each request contains the information needed to understand it; the server does not depend on hidden conversational state between requests.
  3. Cacheability: responses state whether they may be reused by caches.
  4. Uniform interface: resource identification, representations, self-descriptive messages, and (in the strict form) hypermedia controls provide a consistent interaction model.
  5. Layered system: clients need not know whether they are connected directly to the origin or through proxies, gateways, and other intermediaries.
  6. Code-on-demand (optional): a server may extend client behavior by transferring executable code.

An endpoint returning JSON is not automatically RESTful. REST is a set of constraints, not a synonym for “HTTP plus JSON.”

How CRUD maps to HTTP

CRUD intent Common method Typical target Important semantics
Create POST /users Server performs resource-specific processing; generally non-idempotent.
Read GET /users/123 or /users Retrieves a representation and is intended to be safe.
Replace PUT /users/123 Replaces the target representation; idempotent when defined correctly.
Partial update PATCH /users/123 Applies a partial change; idempotency depends on the patch operation.
Delete DELETE /users/123 Removes the target; repeating a successful delete should have the same intended effect.

These are common mappings, not a mandatory CRUD specification. HTTP defines method semantics; your API defines its resource model and representation format. RFC 9110 identifies the request method as the primary source of request semantics, while MDN describes GET as requesting a representation of a specified resource.

REST adds constraints for communication between clients, servers, and intermediaries.
REST adds constraints for communication between clients, servers, and intermediaries.

A complete CRUD request sequence

The following illustrative example uses a hypothetical https://api.example.test/users service. The URI is an example, not a standard mandated by REST. Replace it with your API’s actual base URL.

Create with cURL

curl -i -X POST https://api.example.test/users \
  -H 'Content-Type: application/json' \
  -d '{"name":"Ada Lovelace","email":"ada@example.com"}'

Read a collection and one resource

curl -i https://api.example.test/users
curl -i https://api.example.test/users/123

Replace and partially update

curl -i -X PUT https://api.example.test/users/123 \
  -H 'Content-Type: application/json' \
  -d '{"name":"Ada Lovelace","email":"ada@new.example"}'

curl -i -X PATCH https://api.example.test/users/123 \
  -H 'Content-Type: application/json' \
  -d '{"email":"ada@new.example"}'

Delete

curl -i -X DELETE https://api.example.test/users/123

Runnable Python client

This small client uses the widely available requests package. It checks status codes so transport failures do not look like successful CRUD operations.

import requests

BASE = "https://api.example.test/users"

def show(response):
    response.raise_for_status()
    if response.content:
        print(response.status_code, response.json())
    else:
        print(response.status_code)

created = requests.post(
    BASE,
    json={"name": "Ada Lovelace", "email": "ada@example.com"},
    timeout=30,
)
show(created)
user_id = created.json()["id"]

show(requests.get(BASE, timeout=30))
show(requests.get(f"{BASE}/{user_id}", timeout=30))
show(requests.put(
    f"{BASE}/{user_id}",
    json={"name": "Ada Lovelace", "email": "ada@new.example"},
    timeout=30,
))
show(requests.patch(
    f"{BASE}/{user_id}",
    json={"email": "ada@new.example"},
    timeout=30,
))
show(requests.delete(f"{BASE}/{user_id}", timeout=30))

Runnable Node.js client

Node.js 18 and later include fetch. The helper below accepts any successful 2xx response and prints JSON when the server returns it.

const base = 'https://api.example.test/users';

async function call(path, options = {}) {
  const response = await fetch(path, {
    ...options,
    headers: { 'Content-Type': 'application/json', ...(options.headers || {}) }
  });
  const text = await response.text();
  if (!response.ok) throw new Error(`${response.status}: ${text}`);
  console.log(response.status, text ? JSON.parse(text) : '');
  return text ? JSON.parse(text) : null;
}

const created = await call(base, {
  method: 'POST',
  body: JSON.stringify({ name: 'Ada Lovelace', email: 'ada@example.com' })
});
const id = created.id;
await call(base);
await call(`${base}/${id}`);
await call(`${base}/${id}`, {
  method: 'PUT',
  body: JSON.stringify({ name: 'Ada Lovelace', email: 'ada@new.example' })
});
await call(`${base}/${id}`, {
  method: 'PATCH',
  body: JSON.stringify({ email: 'ada@new.example' })
});
await call(`${base}/${id}`, { method: 'DELETE' });

Why correct verbs are not enough

An API can use GET, POST, PUT, PATCH, and DELETE and still fail to meet REST’s constraints. Review these questions:

  • Resource modeling: Are stable nouns and representations used, or are all actions sent to one command endpoint?
  • Method semantics: Does GET avoid state-changing work? Does PUT replace the representation rather than partially mutate it?
  • Status codes: Are success, client errors, authentication failures, conflicts, and server failures distinguishable?
  • Statelessness: Can a request be understood without an undocumented server session?
  • Cacheability: Do responses provide useful cache directives where safe?
  • Layering: Can gateways, proxies, and caches operate without knowing application internals?
  • Discoverability: Do representations expose links or controls for the next legal actions?

Fielding’s full definition is stricter than the casual industry use of “REST API.” State your conformance level precisely in documentation.

Richardson Maturity Model

Microsoft’s API guidance describes four levels commonly used to discuss REST maturity:

  1. Level 0: one URI and POST for every operation.
  2. Level 1: distinct resource URIs, such as /users/123.
  3. Level 2: HTTP methods and status codes carry their standard meanings.
  4. Level 3: hypermedia controls guide clients through available actions.

Level 2 is common and useful, but calling it fully RESTful can be imprecise if statelessness, cacheability, layering, and hypermedia requirements are absent. Fowler’s explanation of the model is a maturity guide; Fielding’s dissertation remains the reference for the architectural style itself.

CRUD actions that are not simple CRUD

Real domains include operations such as approving an invoice, publishing an article, refreshing a token, or calculating a quote. Forcing each action into a misleading update can make an API harder to understand. Model the domain operation explicitly when it has distinct rules, authorization, validation, or side effects. For example, POST /invoices/123/approval can represent an approval command, while PUT /invoices/123 represents replacement of the invoice representation. Whether that action endpoint fits a strict REST interpretation depends on its resource model and use of the uniform interface.

Idempotency, safety, and retries

Safe methods are intended for retrieval and should not change server state; GET is the primary example. Idempotent means repeating the same request has the same intended effect as sending it once. PUT and DELETE are defined as idempotent in HTTP semantics when implemented correctly. POST is generally not idempotent, so network retries can create duplicates. If clients must safely retry creation or payment-like commands, support an idempotency key and document its retention and conflict behavior. PATCH may be idempotent for a replacement-style patch and non-idempotent for an increment operation.

How to compare two API designs

Axis Questions
Operation coverage Are create, read, replace, partial update, and delete intentions clear?
Resource design Do URIs identify resources rather than verbs or implementation details?
HTTP correctness Do methods, status codes, safety, and idempotency match their standardized meanings?
State handling Can each request be processed without hidden conversational server state?
Caching and intermediaries Can standard caches and gateways safely do useful work?
Discoverability Can a client learn available next actions from representations?
Evolution Can representations and clients change independently without breaking old consumers?

Common mistakes and fixes

“CRUD and REST are interchangeable.”

Cause: both are discussed in API tutorials. Fix: describe CRUD as the operation set and REST as the architectural constraints.

Using POST for every action

Cause: a single command endpoint is easy to implement. Fix: introduce resource URIs and standard method semantics where they clarify intent.

Using PUT for a partial update

Cause: clients send only changed fields. Fix: require a complete replacement for PUT or expose PATCH with a documented patch format.

Retrying POST blindly

Cause: a timeout leaves the client unsure whether the server committed. Fix: use idempotency keys, deduplication, or a status lookup.

Returning 200 for every outcome

Cause: application errors are encoded only in a JSON field. Fix: use HTTP status codes consistently and document the error representation.

Calling an API RESTful because it returns JSON

Cause: JSON is mistaken for an architectural constraint. Fix: review statelessness, cacheability, layering, uniform interface, and discoverability separately.

Reliability, performance, and cost considerations

CRUD versus REST does not determine performance by itself. Latency depends on database queries, serialization, network distance, authentication, and intermediary behavior. Use pagination for collections, field selection when representations are large, conditional requests such as ETags where appropriate, and cache headers for safely reusable responses. Avoid chatty sequences when one well-designed representation can satisfy a screen, but do not create oversized responses that defeat caching and partial retrieval.

Reliability improves when clients distinguish retryable failures (timeouts, some 5xx responses, and rate limits) from permanent validation errors. Set explicit timeouts, apply bounded exponential backoff with jitter, and make retries safe through idempotency. Monitor status classes, latency, saturation, and dependency failures rather than counting only HTTP 200 responses.

Or skip the browser setup

If you need visual documentation of CRUD endpoints, an API workflow, or a rendered reference page, ScreenshotNeo captures a URL with one request. Its clean-shot pipeline accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing result.

See the ScreenshotNeo API documentation for all options. Basic call:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' }); const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. Every plan includes its features; 1,000 screenshots per month are free with no card, and paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

FAQ

Is every CRUD API RESTful?

No. CRUD only identifies data operations. REST requires architectural constraints beyond those operations.

Does REST always use CRUD?

No. REST APIs can model domain actions and workflows that do not map neatly to create, read, update, and delete.

Should I use PUT or PATCH?

Use PUT for a complete replacement and PATCH for a documented partial modification. Define validation and idempotency behavior explicitly.

Can a CRUD system use GraphQL?

Yes. CRUD describes the work being done; it does not require HTTP verbs or REST.

What is the strictest test for “RESTful”?

Evaluate the full set of Fielding’s constraints, including uniform interface and hypermedia, rather than checking verbs and JSON alone.