Logo
A 2026 benchmark of main Web API frameworks

A 2026 benchmark of main Web API frameworks

December 30, 2025
9 min read
index

The contenders

Framework & Source codeRuntimeORM
Laravel 12 (api / image)FrankenPHP 8.5Eloquent
Symfony 8.0 (api / image)FrankenPHP 8.5Doctrine 3
FastAPI (api / image)Python 3.14 (asyncio)SQLAlchemy 2.0 (asyncio)
NestJS 11 (api / image)Node 24Prisma 7
Spring Boot 4.0 (api / image)Java 25Hibernate 6
ASP.NET Core 10 (api / image).NET 10EF Core 10

The Swarm cluster for testing

Cluster architecture

Type of machine used for workers and db storage : cpx32, 4 vCPUs, 8GB RAM.

App deployment configurations

deploy-laravel.yml
version: "3.8"
services:
app:
image: gitea.okami101.io/conduit/laravel:latest
environment:
- APP_KEY=base64:nltxnFb9OaSAr4QcCchy8dG1QXUbc2+2tsXpzN9+ovg=
- DB_CONNECTION=pgsql
- DB_HOST=postgres_db
- DB_USERNAME=okami
- DB_PASSWORD=okami
- DB_DATABASE=conduit_laravel
- JWT_SECRET_KEY=c2b344e1-1a20-47fc-9aef-55b0c0d568a7
networks:
- postgres_db
- traefik_public
deploy:
labels:
- traefik.enable=true
- traefik.http.routers.laravel.entrypoints=websecure
- traefik.http.services.laravel.loadbalancer.server.port=8000
replicas: 2
placement:
max_replicas_per_node: 1
constraints:
- node.labels.run == true
networks:
postgres_db:
external: true
traefik_public:
external: true

The k6 scenarios

Scenario 1 - Database intensive

import http from "k6/http";
import { check } from "k6";
export const options = {
scenarios: {
articles: {
env: { CONDUIT_URL: '<framework_url>' },
duration: '1m',
executor: 'constant-arrival-rate',
rate: '<rate>',
timeUnit: '1s',
preAllocatedVUs: 50,
maxVUs: 200,
},
},
};
export default function () {
const apiUrl = `https://${__ENV.CONDUIT_URL}/api`;
const limit = 10;
let offset = 0;
let articles = []
do {
const articlesResponse = http.get(`${apiUrl}/articles?limit=${limit}&offset=${offset}`);
check(articlesResponse, {
"status is 200": (r) => r.status == 200,
});
articles = articlesResponse.json().articles;
offset += limit;
}
while (articles && articles.length >= limit);
}

The expected pseudocode SQL queries to build this response:

SELECT * FROM articles LIMIT 10 OFFSET 0;
SELECT count(*) FROM articles;
SELECT * FROM users WHERE id IN (<articles.author_id...>);
SELECT * FROM article_tag WHERE article_id IN (<articles.id...>);
SELECT * FROM favorites WHERE article_id IN (<articles.id...>);

Scenario 2 - Runtime intensive

import http from "k6/http";
import { check } from "k6";
export const options = {
scenarios: {
articles: {
env: { CONDUIT_URL: '<framework_url>' },
duration: '1m',
executor: 'constant-arrival-rate',
rate: '<rate>',
timeUnit: '1s',
preAllocatedVUs: 50,
maxVUs: 200,
},
},
};
export default function () {
const apiUrl = `https://${__ENV.CONDUIT_URL}.sw.okami101.io/api`;
const limit = 10;
let offset = 0;
const tagsResponse = http.get(`${apiUrl}/tags`);
check(tagsResponse, {
"status is 200": (r) => r.status == 200,
});
let articles = []
do {
const articlesResponse = http.get(`${apiUrl}/articles?limit=${limit}&offset=${offset}`);
check(articlesResponse, {
"status is 200": (r) => r.status == 200,
});
articles = articlesResponse.json().articles;
for (let i = 0; i < articles.length; i++) {
const article = articles[i];
const articleResponse = http.get(`${apiUrl}/articles/${article.slug}`);
check(articleResponse, {
"status is 200": (r) => r.status == 200,
});
const commentsResponse = http.get(`${apiUrl}/articles/${article.slug}/comments`);
check(commentsResponse, {
"status is 200": (r) => r.status == 200,
});
const authorsResponse = http.get(`${apiUrl}/profiles/${article.author.username}`);
check(authorsResponse, {
"status is 200": (r) => r.status == 200,
});
}
offset += limit;
}
while (articles && articles.length >= limit);
}

The results

Laravel

Laravel scenario 1

Iteration creation rate = 20/s

checks_total.......: 53958 815.756471/s
checks_succeeded...: 100.00% 53958 out of 53958
checks_failed......: 0.00% 0 out of 53958
✓ status is 200
HTTP
http_req_duration..............: avg=152.53ms min=4.51ms med=142.8ms max=459.15ms p(90)=304.87ms p(95)=344.66ms
{ expected_response:true }...: avg=152.53ms min=4.51ms med=142.8ms max=459.15ms p(90)=304.87ms p(95)=344.66ms
http_req_failed................: 0.00% 0 out of 53958
http_reqs......................: 53958 815.756471/s
EXECUTION
dropped_iterations.............: 142 2.146807/s
iteration_duration.............: avg=7.8s min=1.67s med=8.31s max=12.3s p(90)=10.37s p(95)=10.82s
iterations.....................: 1058 15.995225/s
vus............................: 14 min=14 max=190
vus_max........................: 192 min=50 max=192
NETWORK
data_received..................: 575 MB 8.7 MB/s
data_sent......................: 5.1 MB 77 kB/s

Laravel scenario 2

Iteration creation rate = 5/s

checks_total.......: 130227 1446.284124/s
checks_succeeded...: 100.00% 130227 out of 130227
checks_failed......: 0.00% 0 out of 130227
✓ status is 200
HTTP
http_req_duration..............: avg=85.13ms min=2.7ms med=69.64ms max=301.09ms p(90)=187.1ms p(95)=217.36ms
{ expected_response:true }...: avg=85.13ms min=2.7ms med=69.64ms max=301.09ms p(90)=187.1ms p(95)=217.36ms
http_req_failed................: 0.00% 0 out of 130227
http_reqs......................: 130227 1446.284124/s
EXECUTION
dropped_iterations.............: 126 1.39934/s
vus............................: 175 min=5 max=175
vus_max........................: 176 min=50 max=176
NETWORK
data_received..................: 299 MB 3.3 MB/s
data_sent......................: 11 MB 123 kB/s

Symfony

Symfony scenario 1

Iteration creation rate = 25/s

checks_total.......: 71298 1119.920793/s
checks_succeeded...: 100.00% 71298 out of 71298
checks_failed......: 0.00% 0 out of 71298
✓ status is 200
HTTP
http_req_duration..............: avg=86.13ms min=5.77ms med=84.66ms max=246.66ms p(90)=152.69ms p(95)=182.59ms
{ expected_response:true }...: avg=86.13ms min=5.77ms med=84.66ms max=246.66ms p(90)=152.69ms p(95)=182.59ms
http_req_failed................: 0.00% 0 out of 71298
http_reqs......................: 71298 1119.920793/s
EXECUTION
dropped_iterations.............: 103 1.617883/s
iteration_duration.............: avg=4.41s min=1.3s med=4.52s max=7.62s p(90)=6.31s p(95)=6.49s
iterations.....................: 1398 21.959231/s
vus............................: 59 min=25 max=151
vus_max........................: 153 min=50 max=153
NETWORK
data_received..................: 642 MB 10 MB/s
data_sent......................: 6.4 MB 101 kB/s

Symfony scenario 2

Iteration creation rate = 5/s

checks_total.......: 296749 3296.716429/s
checks_succeeded...: 100.00% 296749 out of 296749
checks_failed......: 0.00% 0 out of 296749
✓ status is 200
HTTP
http_req_duration..............: avg=29.34ms min=1.54ms med=25.39ms max=127.11ms p(90)=57.66ms p(95)=64.72ms
{ expected_response:true }...: avg=29.34ms min=1.54ms med=25.39ms max=127.11ms p(90)=57.66ms p(95)=64.72ms
http_req_failed................: 0.00% 0 out of 296749
http_reqs......................: 296749 3296.716429/s
EXECUTION
dropped_iterations.............: 95 1.055397/s
iteration_duration.............: avg=43.02s min=20.22s med=47.91s max=54.99s p(90)=53.93s p(95)=54.4s
iterations.....................: 134 1.488666/s
vus............................: 72 min=5 max=144
vus_max........................: 145 min=50 max=145
NETWORK
data_received..................: 527 MB 5.9 MB/s
data_sent......................: 24 MB 262 kB/s

FastAPI

FastAPI scenario 1

Iteration creation rate = 30/s

checks_total.......: 86904 1378.310382/s
checks_succeeded...: 100.00% 86904 out of 86904
checks_failed......: 0.00% 0 out of 86904
✓ status is 200
HTTP
http_req_duration..............: avg=74.53ms min=4.91ms med=61.65ms max=802.19ms p(90)=118.93ms p(95)=157.88ms
{ expected_response:true }...: avg=74.53ms min=4.91ms med=61.65ms max=802.19ms p(90)=118.93ms p(95)=157.88ms
http_req_failed................: 0.00% 0 out of 86904
http_reqs......................: 86904 1378.310382/s
EXECUTION
dropped_iterations.............: 96 1.522574/s
iteration_duration.............: avg=3.81s min=2.01s med=3.71s max=6.28s p(90)=4.87s p(95)=5.18s
iterations.....................: 1704 27.025694/s
vus............................: 8 min=8 max=143
vus_max........................: 146 min=50 max=146
NETWORK
data_received..................: 708 MB 11 MB/s
data_sent......................: 7.8 MB 124 kB/s

FastAPI scenario 2

Iteration creation rate = 5/s

checks_total.......: 222475 2471.191074/s
checks_succeeded...: 100.00% 222475 out of 222475
checks_failed......: 0.00% 0 out of 222475
✓ status is 200
HTTP
http_req_duration..............: avg=44.78ms min=3.3ms med=36.13ms max=411.67ms p(90)=84.14ms p(95)=113.58ms
{ expected_response:true }...: avg=44.78ms min=3.3ms med=36.13ms max=411.67ms p(90)=84.14ms p(95)=113.58ms
http_req_failed................: 0.00% 0 out of 222475
http_reqs......................: 222475 2471.191074/s
EXECUTION
dropped_iterations.............: 112 1.244065/s
iteration_duration.............: avg=58.31s min=39.18s med=59.92s max=1m12s p(90)=1m11s p(95)=1m11s
iterations.....................: 70 0.777541/s
vus............................: 119 min=5 max=162
vus_max........................: 162 min=50 max=162
NETWORK
data_received..................: 446 MB 5.0 MB/s
data_sent......................: 17 MB 186 kB/s

NestJS

NestJS scenario 1

Iteration creation rate = 50/s

checks_total.......: 150756 2464.488829/s
checks_succeeded...: 100.00% 150756 out of 150756
checks_failed......: 0.00% 0 out of 150756
✓ status is 200
HTTP
http_req_duration..............: avg=29.36ms min=6.07ms med=27.13ms max=166.53ms p(90)=46ms p(95)=52.98ms
{ expected_response:true }...: avg=29.36ms min=6.07ms med=27.13ms max=166.53ms p(90)=46ms p(95)=52.98ms
http_req_failed................: 0.00% 0 out of 150756
http_reqs......................: 150756 2464.488829/s
EXECUTION
dropped_iterations.............: 45 0.735639/s
iteration_duration.............: avg=1.52s min=1.08s med=1.5s max=2.08s p(90)=1.78s p(95)=1.84s
iterations.....................: 2956 48.32331/s
vus............................: 21 min=21 max=90
vus_max........................: 93 min=50 max=93
NETWORK
data_received..................: 2.6 GB 42 MB/s
data_sent......................: 14 MB 227 kB/s

NestJS scenario 2

Iteration creation rate = 5/s

checks_total.......: 358512 4332.501822/s
checks_succeeded...: 99.93% 358281 out of 358512
checks_failed......: 0.06% 231 out of 358512
✗ status is 200
↳ 99% — ✓ 358281 / ✗ 231
HTTP
http_req_duration..............: avg=17.72ms min=1.51ms med=14.96ms max=122.66ms p(90)=32.44ms p(95)=39.51ms
{ expected_response:true }...: avg=17.73ms min=1.51ms med=14.96ms max=122.66ms p(90)=32.45ms p(95)=39.51ms
http_req_failed................: 0.06% 231 out of 358512
http_reqs......................: 358512 4332.501822/s
EXECUTION
dropped_iterations.............: 69 0.833843/s
iteration_duration.............: avg=27.72s min=17.94s med=28.6s max=33.76s p(90)=32.74s p(95)=33.13s
iterations.....................: 231 2.79156/s
vus............................: 9 min=5 max=119
vus_max........................: 119 min=50 max=119
NETWORK
data_received..................: 1.6 GB 20 MB/s
data_sent......................: 29 MB 348 kB/s

Spring Boot

Spring Boot scenario 1

Iteration creation rate = 60/s

checks_total.......: 181101 2978.075175/s
checks_succeeded...: 100.00% 181101 out of 181101
checks_failed......: 0.00% 0 out of 181101
✓ status is 200
HTTP
http_req_duration..............: avg=21.08ms min=2.28ms med=15.76ms max=185.05ms p(90)=41.1ms p(95)=50.77ms
{ expected_response:true }...: avg=21.08ms min=2.28ms med=15.76ms max=185.05ms p(90)=41.1ms p(95)=50.77ms
http_req_failed................: 0.00% 0 out of 181101
http_reqs......................: 181101 2978.075175/s
EXECUTION
dropped_iterations.............: 50 0.822214/s
iteration_duration.............: avg=1.1s min=518.89ms med=1.09s max=2.03s p(90)=1.54s p(95)=1.64s
iterations.....................: 3551 58.393631/s
vus............................: 97 min=34 max=98
vus_max........................: 100 min=55 max=100
NETWORK
data_received..................: 3.4 GB 56 MB/s
data_sent......................: 16 MB 255 kB/s

Spring Boot scenario 2

Iteration creation rate = 5/s

checks_total.......: 361616 4497.264215/s
checks_succeeded...: 100.00% 361616 out of 361616
checks_failed......: 0.00% 0 out of 361616
✓ status is 200
HTTP
http_req_duration..............: avg=16.59ms min=1.7ms med=13ms max=265.36ms p(90)=32.5ms p(95)=41.83ms
{ expected_response:true }...: avg=16.59ms min=1.7ms med=13ms max=265.36ms p(90)=32.5ms p(95)=41.83ms
http_req_failed................: 0.00% 0 out of 361616
http_reqs......................: 361616 4497.264215/s
EXECUTION
dropped_iterations.............: 68 0.845687/s
iteration_duration.............: avg=26s min=12.3s med=27.08s max=33.23s p(90)=32.11s p(95)=32.42s
iterations.....................: 233 2.897722/s
vus............................: 6 min=5 max=116
vus_max........................: 118 min=50 max=118
NETWORK
data_received..................: 1.6 GB 20 MB/s
data_sent......................: 32 MB 396 kB/s

ASP.NET Core

ASP.NET Core scenario 1

Iteration creation rate = 45/s

checks_total.......: 137700 2276.867791/s
checks_succeeded...: 100.00% 137700 out of 137700
checks_failed......: 0.00% 0 out of 137700
✓ status is 200
HTTP
http_req_duration..............: avg=14.75ms min=3.1ms med=13.93ms max=89.59ms p(90)=21.13ms p(95)=23.82ms
{ expected_response:true }...: avg=14.75ms min=3.1ms med=13.93ms max=89.59ms p(90)=21.13ms p(95)=23.82ms
http_req_failed................: 0.00% 0 out of 137700
http_reqs......................: 137700 2276.867791/s
EXECUTION
iteration_duration.............: avg=780.66ms min=498.88ms med=773.39ms max=1.1s p(90)=888.81ms p(95)=928.54ms
iterations.....................: 2700 44.644466/s
vus............................: 30 min=29 max=46
vus_max........................: 50 min=50 max=50
NETWORK
data_received..................: 3.1 GB 52 MB/s
data_sent......................: 12 MB 201 kB/s

ASP.NET Core scenario 2

Iteration creation rate = 5/s

checks_total.......: 383344 5099.848651/s
checks_succeeded...: 100.00% 383344 out of 383344
checks_failed......: 0.00% 0 out of 383344
✓ status is 200
HTTP
http_req_duration..............: avg=13.64ms min=1.27ms med=11.96ms max=235.6ms p(90)=23.93ms p(95)=28.28ms
{ expected_response:true }...: avg=13.64ms min=1.27ms med=11.96ms max=235.6ms p(90)=23.93ms p(95)=28.28ms
http_req_failed................: 0.00% 0 out of 383344
http_reqs......................: 383344 5099.848651/s
EXECUTION
dropped_iterations.............: 53 0.70509/s
iteration_duration.............: avg=21.41s min=15.48s med=21.44s max=25.06s p(90)=24.41s p(95)=24.66s
iterations.....................: 247 3.285985/s
vus............................: 2 min=2 max=102
vus_max........................: 103 min=50 max=103
NETWORK
data_received..................: 2.3 GB 31 MB/s
data_sent......................: 34 MB 456 kB/s

Conclusion

Here are the final req/s results for each framework against PgSQL database.