Una alternativa a JSONPlaceholder donde las escrituras se guardan
Los mismos tipos de recursos, pero las escrituras se guardan, la entrada mala se rechaza y los errores están disponibles cuando quieras.
Ejemplos ejecutados por última vez contra la API el
JSONPlaceholder finge sus escrituras: un POST devuelve id: 101, pero un GET de ese id devuelve 404, y un DELETE deja el registro donde estaba. Si tu app necesita volver a leer lo que acaba de escribir, usa https://api.testingapis.com/apiinicia sesión para verlo. Tiene los mismos tipos de recursos (posts, comentarios, álbumes, fotos, tareas y usuarios), y crear, actualizar y borrar se guarda de verdad. Además rechaza la entrada inválida con un 400 y puede hacer fallar cualquier petición cuando quieras, así que también puedes construir y probar los estados de error. Es gratuita, no necesita clave y acepta peticiones desde cualquier origen.
Escrituras que se guardan
Esto es lo que hace JSONPlaceholder con un post nuevo:
$ curl -X POST https://jsonplaceholder.typicode.com/posts \
-H "Content-Type: application/json" \
-d '{"title":"hello","body":"world","userId":1}'
{ "title": "hello", "body": "world", "userId": 1, "id": 101 }
$ curl -i https://jsonplaceholder.typicode.com/posts/101
HTTP/1.1 404 Not FoundLos mismos pasos aquí: crear un post, volver a leerlo, editarlo, borrarlo y comprobar que ha desaparecido.
$ curl -X POST https://api.testingapis.com/api/Posts \
-H "Content-Type: application/json" \
-d '{"userId":101,"title":"Hello from the guide","body":"Checking that writes persist."}'
{"message":"Post created successfully","data":{"id":16,"userId":101,"title":"Hello from the guide","body":"Checking that writes persist.","tags":[],"publishedAt":"2026-10-07T02:58:58Z","likes":0}}
$ curl https://api.testingapis.com/api/Posts/16
{"id":16,"userId":101,"title":"Hello from the guide","body":"Checking that writes persist.","tags":[],"publishedAt":"2026-10-07T02:58:58Z","likes":0}
$ curl -X PUT https://api.testingapis.com/api/Posts/16 \
-H "Content-Type: application/json" \
-d '{"userId":101,"title":"Edited title","body":"Checking that writes persist."}'
{"message":"Post updated successfully","data":{"id":16,"userId":101,"title":"Edited title",...}}
$ curl -X DELETE https://api.testingapis.com/api/Posts/16
{"message":"Post deleted successfully"}
$ curl -i https://api.testingapis.com/api/Posts/16
HTTP/1.1 404 Not Found
{"status":404,"error":"Not Found","message":"Post with ID 16 does not exist."}Equivalencia de rutas
| JSONPlaceholder | testingapis.com | Registros |
|---|---|---|
/posts | /api/posts | 15 (JSONPlaceholder: 100) |
/posts/1/comments | /api/posts/1/comments | Comentarios del post 1 |
/comments | /api/commentsdocs | 50 (500) |
/albums | /api/albumsdocs | 10 (100) |
/albums/1/photos | /api/albums/1/photosinicia sesión para verlo | 5 por álbum |
/photos | /api/photosdocs | 50 (5.000) |
/todos | /api/todos | 30 (200) |
/users | /api/peopledocs | 20 (10), con dirección, coordenadas y empresa |
/users/1/posts | /api/posts/user/101 | Los ids de usuario empiezan en 101 |
Las rutas no distinguen mayúsculas: /api/posts y /api/Posts son lo mismo. Hay menos registros que en JSONPlaceholder, pero cada uno tiene más campos. Los posts tienen etiquetas, likes y fecha de publicación, las tareas tienen prioridad y fecha límite, y las personas tienen una dirección y una empresa anidadas. La documentación de la API lista 13 tipos de recurso más, desde productos y carritos hasta libros, películas y países.
/api/posts/1La entrada mala recibe un error
JSONPlaceholder acepta cualquier cuerpo, incluso uno vacío. Aquí, un campo obligatorio que falta devuelve un 400 con un mensaje, así que la rama de error de tu formulario tiene algo real que mostrar:
$ curl -i -X POST https://jsonplaceholder.typicode.com/posts -H "Content-Type: application/json" -d '{}'
HTTP/1.1 201 Created
{ "id": 101 }
$ curl -i -X POST https://api.testingapis.com/api/Posts -H "Content-Type: application/json" -d '{"title":""}'
HTTP/1.1 400 Bad Request
{"status":400,"error":"Bad Request","message":"Missing required field: title"}Errores y respuestas lentas cuando quieras
Añade ?error=500 a cualquier petición para que falle con ese estado, o ?delay=3 para que responda tres segundos tarde. Juntos, ?delay=2&error=503 muestra un estado de carga seguido de un error. La guía de respuestas de error cubre timeouts, reintentos y límites de frecuencia.
/api/todos/1?error=503Filtros, ordenación y paginación
Las dos APIs filtran listas por campo, como en ?userId=101, y las dos ordenan, buscan y paginan. Los nombres de los parámetros cambian:
| JSONPlaceholder | testingapis.com | |
|---|---|---|
| Filtrar | ?userId=1 | ?userId=101 |
| Paginar | ?_page=2&_limit=10 | ?page=2&limit=10 |
| Ordenar | ?_sort=id&_order=desc | ?sort=id&order=desc |
| Buscar | ?q=word | ?q=word |
| Total | X-Total-Count | X-Total-Count, X-Total-Pages |
Pasar una app de una a otra
Las lecturas devuelven el registro directamente, como JSONPlaceholder. Las escrituras lo envuelven con un mensaje, como { message, data }, y los ids de usuario empiezan en 101. Esos son los dos cambios que necesitan casi todas las apps:
// Before
const BASE = "https://jsonplaceholder.typicode.com";
const post = await (await fetch(`${BASE}/posts/1`)).json();
const created = await (await fetch(`${BASE}/posts`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userId: 1, title: "Hi", body: "There" }),
})).json();
// After
const BASE = "https://api.testingapis.com/api";
const post = await (await fetch(`${BASE}/posts/1`)).json();
const { data: created } = await (await fetch(`${BASE}/posts`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userId: 101, title: "Hi", body: "There" }),
})).json();Cuándo es mejor JSONPlaceholder
- Necesitas listas grandes, como 5.000 fotos para probar el scroll virtual.
- Sigues un tutorial que usa exactamente los ids y campos de JSONPlaceholder.
- Solo lees datos y nunca necesitas que una escritura aparezca después.
Para ir más allá, la lección de testing CRUD explica cómo comprobar que cada creación, actualización y borrado ocurrió de verdad.