Skip to content
JSON, Twitter, and the Richardson Maturity Model: REST Goes Mainstream 12 min

JSON, Twitter, and the Richardson Maturity Model: REST Goes Mainstream

ScaleDojo
ScaleDojo
•
May 23, 2026
•
12 min read
•2,599 words
JSON, Twitter, and the Richardson Maturity Model: REST Goes Mainstream

JSON: The Accidental Standard

Douglas Crockford did not invent JSON. JavaScript had always been able to parse JavaScript object literals. Crockford noticed in 2001 that a JavaScript object literal without any additional code was valid JavaScript that browsers would evaluate as data. He registered json.org, wrote a short spec, and called it JSON (JavaScript Object Notation). He named it, documented it, and advocated for it. The format already existed.

JSON had one superpower over XML: a JavaScript developer could call JSON.parse() on a string and get back a native object with no libraries, no schemas, no configuration. The same data that required an XML parser in 2003 required zero external code in 2006. As Ajax made browser-side JavaScript mainstream, JSON became the obvious format for API responses. It was not more capable than XML. It was dramatically easier to work with.

Twitter's API: The Template for Public REST

Twitter launched its public API in 2006 alongside the site itself - remarkably, a significant portion of early Twitter usage was through the API rather than the website. The Twitter API established the conventions that every REST API has followed since: resource-oriented URLs (/statuses/show/:id), HTTP verbs for operations, JSON responses, OAuth for authentication, rate limiting by API key, pagination with cursors, and human-readable error messages.

By 2009, Twitter had more API clients than it had web clients. Tweetdeck, HootSuite, Twitterrific, Seesmic - dozens of applications built on the Twitter API. This was the first proof at scale that an API could be a company's primary interface with its users. The website was just one client among many.

The Richardson Maturity Model

In 2008, Leonard Richardson presented a model for categorizing REST APIs into four levels. Level 0: one URL, one HTTP method (like SOAP). Level 1: multiple resource URLs, still one method. Level 2: multiple URLs plus proper HTTP verb usage. Level 3: HATEOAS - hypermedia controls embedded in responses that tell clients what actions are available next.

Most public REST APIs live at Level 2. They have resource URLs and proper verbs. They return status codes correctly. Very few reach Level 3 (HATEOAS) in practice - the overhead of including hypermedia links rarely pays off for typical client-server applications. Knowing where your API sits on this scale helps you decide what to invest in.

What These Three Things Built Together

JSON made REST accessible. Twitter made it a proven pattern. The Richardson model gave developers a vocabulary to discuss API quality. By 2010, REST+JSON was the default for any new public API, and developers who had spent years writing SOAP clients were quietly grateful for the simplification.

JSON's Evolution into a Type System

JSON started as a format with no types beyond string, number, boolean, null, array, and object. JSON Schema (draft-01, 2010) added type constraints, required fields, string patterns, numeric ranges, and array item types. JSON Schema is now used everywhere that JSON payloads need validation: OpenAPI request/response schemas, form validation, configuration file validation, Kafka message schemas, and database JSONB column constraints. The evolution from 'no types' to 'optional type annotations in a separate schema file' mirrors how JavaScript evolved from untyped to TypeScript.

JSON's number type has a well-known precision problem: it does not distinguish integers from floats, and JavaScript's number type is a 64-bit float that cannot represent all 64-bit integers exactly. A user ID of 9223372036854775807 (max int64) becomes 9223372036854775808 after a JSON round-trip through JavaScript. Twitter ran into this with tweet IDs in 2013 and solved it by returning IDs as both numbers and strings in their API responses. The correct solution for large integer IDs in JSON APIs is to use strings.

Twitter's API Breakdown and What it Taught

Twitter's API history is also a cautionary tale about API governance. The v1.0 API was open and permissive: third-party clients were welcomed, and most of Twitter's ecosystem ran on API access. The v1.1 API in 2012 introduced rate limits and required OAuth authentication for all requests. Access was throttled by application and tied to user tokens. Third-party clients that had built businesses on open API access found themselves constrained overnight. Some shut down. The developer community, which had built Twitter's ecosystem, felt betrayed.

This pattern - open API to build ecosystem, then tighten terms once the ecosystem is dependent - is common enough that it has a name: rug pull. Musk's 2023 elimination of free API access accelerated what had been a gradual tightening since 2012. The lesson for API designers: the terms you establish when developers adopt your API create obligations. Changing them is a relationship decision, not just a technical one. Developer trust is hard to earn and easy to destroy.

  • Use strings for all IDs in JSON APIs. Numbers lose precision for large integers in JavaScript environments.
  • Pagination via cursors (next_cursor token) is more stable than offset pagination for frequently-changing data sets.
  • Rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset) should be included on every response so clients can self-throttle.
  • Error responses should include a machine-readable error code AND a human-readable message. The code lets client code handle the error programmatically. The message helps the developer debugging.
  • Webhook event payloads should include a full object snapshot, not just the changed fields. Clients should not need to make a follow-up GET request after every webhook.

JSON Schema and the Validation Layer

JSON Schema fills the gap that pure JSON left: type enforcement. A JSON string with value '123' and a JSON number with value 123 are different - but if your API accepts 'amount': '100.00' when it expects 'amount': 100.00, your payment logic may silently truncate or fail. JSON Schema lets you express constraints: amount must be a number, must be positive, must have at most 2 decimal places. FastAPI and Pydantic in Python, Express Validator in Node, and Zod in TypeScript all implement JSON Schema validation (or equivalents) to enforce these constraints at the API boundary rather than letting invalid data propagate into business logic.

Request validation that runs before your business logic handler is a security and correctness requirement, not just a convenience. SQL injection, prototype pollution, and integer overflow attacks often succeed because the API trusted input that should have been rejected at the boundary. A strict JSON Schema that rejects unexpected fields (additionalProperties: false), enforces types, and bounds numeric values eliminates a class of vulnerabilities before they can reach the application logic.

The Richardson Maturity Model Level 3 (Hypermedia Controls, also called HATEOAS) is widely discussed and rarely implemented. The theory is compelling: an API that returns links to available actions with each resource is self-describing, and clients do not need to hardcode URL patterns. The practice is that HATEOAS requires clients to navigate links rather than construct URLs, which increases client complexity and breaks most HTTP caching strategies because link URLs can change. Most production REST APIs are Level 2 (resources plus HTTP verbs) and document their URL patterns in OpenAPI specs. Level 2 is the pragmatic standard.

JSON's Evolution into a Type System

JSON started as a format with no types beyond string, number, boolean, null, array, and object. JSON Schema (draft-01, 2010) added type constraints, required fields, string patterns, numeric ranges, and array item types. JSON Schema is now used everywhere that JSON payloads need validation: OpenAPI request/response schemas, form validation, configuration file validation, Kafka message schemas, and database JSONB column constraints. The evolution from 'no types' to 'optional type annotations in a separate schema file' mirrors how JavaScript evolved from untyped to TypeScript.

JSON's number type has a well-known precision problem: it does not distinguish integers from floats, and JavaScript's number type is a 64-bit float that cannot represent all 64-bit integers exactly. A user ID of 9223372036854775807 (max int64) becomes 9223372036854775808 after a JSON round-trip through JavaScript. Twitter ran into this with tweet IDs in 2013 and solved it by returning IDs as both numbers and strings in their API responses. The correct solution for large integer IDs in JSON APIs is to use strings.

Twitter's API Breakdown and What it Taught

Twitter's API history is also a cautionary tale about API governance. The v1.0 API was open and permissive: third-party clients were welcomed, and most of Twitter's ecosystem ran on API access. The v1.1 API in 2012 introduced rate limits and required OAuth authentication for all requests. Access was throttled by application and tied to user tokens. Third-party clients that had built businesses on open API access found themselves constrained overnight. Some shut down. The developer community, which had built Twitter's ecosystem, felt betrayed.

This pattern - open API to build ecosystem, then tighten terms once the ecosystem is dependent - is common enough that it has a name: rug pull. Musk's 2023 elimination of free API access accelerated what had been a gradual tightening since 2012. The lesson for API designers: the terms you establish when developers adopt your API create obligations. Changing them is a relationship decision, not just a technical one. Developer trust is hard to earn and easy to destroy.

  • Use strings for all IDs in JSON APIs. Numbers lose precision for large integers in JavaScript environments.
  • Pagination via cursors (next_cursor token) is more stable than offset pagination for frequently-changing data sets.
  • Rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset) should be included on every response so clients can self-throttle.
  • Error responses should include a machine-readable error code AND a human-readable message. The code lets client code handle the error programmatically. The message helps the developer debugging.
  • Webhook event payloads should include a full object snapshot, not just the changed fields. Clients should not need to make a follow-up GET request after every webhook.

JSON Schema and the Validation Layer

JSON Schema fills the gap that pure JSON left: type enforcement. A JSON string with value '123' and a JSON number with value 123 are different - but if your API accepts 'amount': '100.00' when it expects 'amount': 100.00, your payment logic may silently truncate or fail. JSON Schema lets you express constraints: amount must be a number, must be positive, must have at most 2 decimal places. FastAPI and Pydantic in Python, Express Validator in Node, and Zod in TypeScript all implement JSON Schema validation (or equivalents) to enforce these constraints at the API boundary rather than letting invalid data propagate into business logic.

Request validation that runs before your business logic handler is a security and correctness requirement, not just a convenience. SQL injection, prototype pollution, and integer overflow attacks often succeed because the API trusted input that should have been rejected at the boundary. A strict JSON Schema that rejects unexpected fields (additionalProperties: false), enforces types, and bounds numeric values eliminates a class of vulnerabilities before they can reach the application logic.

The Richardson Maturity Model Level 3 (Hypermedia Controls, also called HATEOAS) is widely discussed and rarely implemented. The theory is compelling: an API that returns links to available actions with each resource is self-describing, and clients do not need to hardcode URL patterns. The practice is that HATEOAS requires clients to navigate links rather than construct URLs, which increases client complexity and breaks most HTTP caching strategies because link URLs can change. Most production REST APIs are Level 2 (resources plus HTTP verbs) and document their URL patterns in OpenAPI specs. Level 2 is the pragmatic standard.

JSON's Evolution into a Type System

JSON started as a format with no types beyond string, number, boolean, null, array, and object. JSON Schema (draft-01, 2010) added type constraints, required fields, string patterns, numeric ranges, and array item types. JSON Schema is now used everywhere that JSON payloads need validation: OpenAPI request/response schemas, form validation, configuration file validation, Kafka message schemas, and database JSONB column constraints. The evolution from 'no types' to 'optional type annotations in a separate schema file' mirrors how JavaScript evolved from untyped to TypeScript.

JSON's number type has a well-known precision problem: it does not distinguish integers from floats, and JavaScript's number type is a 64-bit float that cannot represent all 64-bit integers exactly. A user ID of 9223372036854775807 (max int64) becomes 9223372036854775808 after a JSON round-trip through JavaScript. Twitter ran into this with tweet IDs in 2013 and solved it by returning IDs as both numbers and strings in their API responses. The correct solution for large integer IDs in JSON APIs is to use strings.

Twitter's API Breakdown and What it Taught

Twitter's API history is also a cautionary tale about API governance. The v1.0 API was open and permissive: third-party clients were welcomed, and most of Twitter's ecosystem ran on API access. The v1.1 API in 2012 introduced rate limits and required OAuth authentication for all requests. Access was throttled by application and tied to user tokens. Third-party clients that had built businesses on open API access found themselves constrained overnight. Some shut down. The developer community, which had built Twitter's ecosystem, felt betrayed.

This pattern - open API to build ecosystem, then tighten terms once the ecosystem is dependent - is common enough that it has a name: rug pull. Musk's 2023 elimination of free API access accelerated what had been a gradual tightening since 2012. The lesson for API designers: the terms you establish when developers adopt your API create obligations. Changing them is a relationship decision, not just a technical one. Developer trust is hard to earn and easy to destroy.

  • Use strings for all IDs in JSON APIs. Numbers lose precision for large integers in JavaScript environments.
  • Pagination via cursors (next_cursor token) is more stable than offset pagination for frequently-changing data sets.
  • Rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset) should be included on every response so clients can self-throttle.
  • Error responses should include a machine-readable error code AND a human-readable message. The code lets client code handle the error programmatically. The message helps the developer debugging.
  • Webhook event payloads should include a full object snapshot, not just the changed fields. Clients should not need to make a follow-up GET request after every webhook.

JSON Schema and the Validation Layer

JSON Schema fills the gap that pure JSON left: type enforcement. A JSON string with value '123' and a JSON number with value 123 are different - but if your API accepts 'amount': '100.00' when it expects 'amount': 100.00, your payment logic may silently truncate or fail. JSON Schema lets you express constraints: amount must be a number, must be positive, must have at most 2 decimal places. FastAPI and Pydantic in Python, Express Validator in Node, and Zod in TypeScript all implement JSON Schema validation (or equivalents) to enforce these constraints at the API boundary rather than letting invalid data propagate into business logic.

Request validation that runs before your business logic handler is a security and correctness requirement, not just a convenience. SQL injection, prototype pollution, and integer overflow attacks often succeed because the API trusted input that should have been rejected at the boundary. A strict JSON Schema that rejects unexpected fields (additionalProperties: false), enforces types, and bounds numeric values eliminates a class of vulnerabilities before they can reach the application logic.

The Richardson Maturity Model Level 3 (Hypermedia Controls, also called HATEOAS) is widely discussed and rarely implemented. The theory is compelling: an API that returns links to available actions with each resource is self-describing, and clients do not need to hardcode URL patterns. The practice is that HATEOAS requires clients to navigate links rather than construct URLs, which increases client complexity and breaks most HTTP caching strategies because link URLs can change. Most production REST APIs are Level 2 (resources plus HTTP verbs) and document their URL patterns in OpenAPI specs. Level 2 is the pragmatic standard.

Enjoyed this article?

Share it with your network to help others level up their system design skills.

Discussion0

Join the Discussion

Sign in to leave comments, reply to others, or like insights.

Sign In to ScaleDojo

No comments yet. Be the first to start the thread!

Enjoyed this? Get more like it.

New levels, features, and one concept worth knowing - straight to your inbox.

No spam, ever. Unsubscribe in one click.

Enjoyed this content?

Your support keeps us creating free resources

We put a lot of hours into researching and writing these guides. If it helped you, consider buying us a coffee. Every bit goes toward keeping ScaleDojo's content free and growing.

$

One-time payment via Stripe. ScaleDojo account required.