Skip to main content

Rubric

Rubric is a CLI tool for testing APIs using their OpenAPI schema.

It reads the API's /openapi.json, generates request payloads with an LLM, sends the requests concurrently, and records response times and status codes. It can also use the failed requests to generate a short diagnostic report.

How it works

  1. Read the OpenAPI schema Rubric loads /openapi.json and identifies the available routes, methods, parameters, and request bodies.

  2. Generate test payloads An LLM generates payloads for each endpoint. The payloads are split into valid inputs, edge cases, and malformed inputs.

  3. Run the requests Requests are sent asynchronously with a configurable concurrency limit. Progress and response statistics are shown in the terminal.

  4. Generate the report Rubric records latency percentiles, response status codes, error rates, and endpoint results. Failed requests can also be passed to the LLM for a short explanation.

Route buckets

Bucket Methods Description
A GET, DELETE Endpoints without a request body
B POST, PUT, PATCH Endpoints that accept a request body
C Any Endpoints that need an ID created by an earlier request

Payload tiers

Rubric automatically alters its grading logic depending on the tier you are testing:

Tier Count Description / Grading Rule
happy_path 17 Valid data. Expects a 2xx success code. 4xx and 500 are failures.
edge_cases 17 Boundary limits (e.g. max-length strings). Expects 2xx or 4xx. 500 is a failure.
malformed 16 Invalid data. Expects a 4xx rejection. 2xx (data accepted) or 500 are failures.

Installation

Requires Python 3.11+.

pip install rubric-load-tester

Windows users: To avoid Windows Defender interfering with the .exe wrapper pip creates, you can run the CLI directly via Python:

python -m rubric run <url>

Copy the environment file:

cp .env.example .env

Then configure the LLM provider:

LLM_PROVIDER=groq

GROQ_API_KEY=your_groq_key_here
GROQ_MODEL=meta-llama/llama-4-scout-17b-16e-instruct

OPENAI_API_KEY=your_openai_key_here
OPENAI_MODEL=gpt-4o
OPENAI_BASE_URL=https://api.openai.com/v1

Usage

Run against a local API:

rubric run http://localhost:8000

Specify the request count, concurrency, payload tier, and thresholds:

rubric run http://localhost:8000 \
  --users 50 \
  --tier happy_path \

Run all payload tiers:

rubric run http://localhost:8000 --tier all --users 100

Inspect the API without running the load test:

rubric inspect http://localhost:8000

Disable the LLM diagnostic step:

rubric run http://localhost:8000 --no-diagnostics

Choose a provider for a single run:

rubric run http://localhost:8000 --provider groq
rubric run http://localhost:8000 --provider openai

Authentication

Rubric supports interactive and automated authentication flows for APIs requiring authorization. During the first run against a protected API, Rubric will interview you to acquire credentials and test them before starting the load test.

Supported schemes:

  • Bearer / JWT
  • Basic Auth
  • API Keys (Header-based)
  • Cookie-based sessions
  • OAuth2 (Client flows)

Tokens are cached locally in .rubric/auth.json (for local dev) and refreshed silently when they expire. You can also bypass the interview by providing a token directly:

rubric run http://localhost:8000 --token "your-jwt-here"

Self-signed certificates

For local development servers using a self-signed certificate:

rubric run https://localhost:8443 --allow-self-signed

This should only be used against a server you control. The option allows Rubric to continue when the target certificate cannot be verified.

Command options

Flag Default Description
--users 10 Target concurrent users (Closed Workload Model)
--rps - Target requests per second (Open Workload Model)
--tier happy_path happy_path, edge_cases, malformed, or all
--assert - Custom assertions (e.g., "status == 200")
--no-diagnostics False Disable LLM failure diagnostics
--save True Save the JSON report
--provider .env value Override the configured LLM provider
--allow-self-signed False Allow unverified TLS certificates

Output

During a run, Rubric displays the current request and response statistics:

RUBRIC · 847 requests · 142.3 RPS · 5.9s

┌────────────────────────┬──────┬──────┬──────┬──────┬──────┬───────┐
│ Endpoint               │ RPS  │ P95  │ 2xx  │ 4xx  │ 5xx  │ Total │
├────────────────────────┼──────┼──────┼──────┼──────┼──────┼───────┤
│ POST /users            │ 142  │ 84ms │ 891  │ 23   │ 6    │ 920   │
│ GET /users/{user_id}   │ 89   │ 31ms │ 412  │ 0    │ 1    │ 413   │
│ POST /products         │ 201  │ 19ms │ 1204 │ 11   │ 0    │ 1215  │
└────────────────────────┴──────┴──────┴──────┴──────┴──────┴───────┘

At the end of the run, each endpoint is classified as PASSED, DEGRADED, or FAILED based on the configured thresholds.

┌────────────────────────┬────────────┬─────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐
│ Endpoint               │ Status     │ RPS │ P50  │ P95  │ P99  │ 2xx  │ 4xx  │ 5xx  │ Err% │
├────────────────────────┼────────────┼─────┼──────┼──────┼──────┼──────┼──────┼──────┼──────┤
│ POST /users            │ PASSED     │ 142 │ 34ms │ 84ms │201ms │ 891  │ 23   │ 6    │0.67% │
│ GET /users/{user_id}   │ DEGRADED  │ 89  │ 12ms │312ms │ 67ms │ 412  │ 0    │ 1    │0.24% │
│ POST /products         │ FAILED     │ 201 │ 18ms │ 44ms │ 98ms │ 980  │ 11   │ 24   │2.10% │
└────────────────────────┴────────────┴─────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┘

SUITE FAILED

Passed: 1
Degraded: 1
Failed: 1

Total: 2507 requests
Duration: 17.6s
RPS: 142.4

Thresholds:
P95 < 200ms
Error rate < 1%

With diagnostics enabled, failed requests can also include an explanation:

1. POST /users

   Payload: {"name": "", "email": "x@y.com", "role": ""}

   Cause: empty 'role' string reaches the database layer.

   Fix: validate the field before sending the request.

Sample API

The repository includes a small FastAPI application for testing Rubric locally.

Install FastAPI and Uvicorn:

pip install fastapi uvicorn

Start the sample API:

uvicorn sample_api:app --reload

Then run Rubric in another terminal:

rubric run http://localhost:8000

Or inspect the API first:

rubric inspect http://localhost:8000

JSON reports

Rubric saves a JSON report after each run:

rubric_report_<url>_<timestamp>.json

Example:

{
  "suite_result": "SUITE FAILED",
  "thresholds": {
    "p95_ms": 200,
    "error_rate_pct": 1.0
  },
  "summary": {
    "total_requests": 2507,
    "duration_seconds": 17.6,
    "overall_rps": 142.4,
    "connection_drops": 0,
    "passed": 1,
    "degraded": 1,
    "failed": 1
  },
  "endpoints": [],
  "failure_diagnostics": []
}

SUITE FAILED is also used as the CI result. Rubric exits with status code 1 when the configured thresholds are exceeded.

Project structure

rubric/
├── core/
│   ├── schema.py
│   ├── synthesizer.py
│   └── llm.py
├── engine/
│   └── runner.py
├── report/
│   └── reporter.py
├── cli/
│   └── main.py
├── sample_api.py
├── requirements.txt
├── setup.py
└── .env.example

Main components

  • core/schema.py - reads the OpenAPI schema and categorizes routes
  • core/synthesizer.py - generates request payloads
  • core/llm.py - LLM provider handling
  • engine/runner.py - sends requests and collects metrics
  • report/reporter.py - builds reports and diagnostics
  • cli/main.py - CLI entry point
  • sample_api.py - local API used for testing

Security notes

Data sent to the LLM

When diagnostics are enabled, Rubric sends information about failed requests to the configured LLM provider.

This includes:

  • the endpoint
  • a redacted request payload
  • the first 300 characters of the response

Rubric attempts to remove values from fields such as password, token, and similar secret-looking fields. It also looks for JWTs, bearer tokens, API keys, and email addresses.

This redaction is not guaranteed to catch every sensitive value. If the target API can return sensitive information in error responses, use:

rubric run http://localhost:8000 --no-diagnostics

Target reset

Rubric does not call /reset by default.

The reset endpoint is included for the sample API. If you need it:

rubric run http://localhost:8000 --reset-target

or:

RUBRIC_ALLOW_RESET=1

Only enable this when testing an environment where resetting the data is acceptable.

Credentials

Rubric stores its local configuration and authentication files with owner-only permissions:

~/.rubric/config.json
.rubric/auth.json

For CI or other non-interactive runs, stored passwords are removed after the run.

For authentication, prefer:

RUBRIC_AUTH_TOKEN=...

over passing a token directly on the command line.

Reports

Values originating from the target API or the LLM are HTML-escaped before being included in reports.

The generated report also includes a restrictive Content Security Policy.

Metadata

Release files for rubric-load-tester 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rubric-load-tester 0.3.0
File Size Uploaded
rubric_load_tester-0.3.0.tar.gz 234.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rubric-load-tester 0.3.0
File Interpreter ABI Platform
rubric_load_tester-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 470.9 kB

Release files / rubric_load_tester-0.3.0.tar.gz

Download URL rubric_load_tester-0.3.0.tar.gz
Size 234.3 kB
Tags Source
SHA-256 checksum
How to use checksums
b4a2e4bbf67b41ac2e1c0f20cced343912c0b84956b7ac8b69ca405eb920daa1
BLAKE2b-256 checksum
How to use checksums
80253ed2fc7c69f1bf382800e4bed3cd1089242222ebf7b54f6ca98e04e6b159
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Release files / rubric_load_tester-0.3.0-py3-none-any.whl

Download URL rubric_load_tester-0.3.0-py3-none-any.whl
Size 236.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
87021599d5435545f1edaf85485b99c86679a0233f8cfa141e7083461df1c0a2
BLAKE2b-256 checksum
How to use checksums
5840e3b1b34aaac235e85d86a9f0a7ea0f1fb21f58df60ace3f2d3cade7946bc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Release history Release notifications | RSS feed

0.3.1

2 release files

This release

0.3.0 This release

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page