How to Report API Bugs: Template, Examples and Severity
Finding an API bug is half the job. The other half is reporting it so clearly that a developer can reproduce it in one minute, understand the impact and fix the right thing. A vague ticket like “Order API not working” bounces back and forth for days; a precise one with a cURL command gets fixed the same day. In this lesson you’ll learn the API bug report template that developers love, how to judge severity and priority, and see real examples, including genuine issues found during this course.
Why API Bug Reports Are Different
UI bugs can be shown with a screenshot. API bugs live in requests and responses that nobody can see unless you capture them. That means:
- The exact request matters: one header or one field type changes everything.
- The exact response (status, headers, body) is the evidence.
- Backend logs are searchable by request id and timestamp, so include them.
- The same API serves web, Android, iOS and partners, so state which client or whether it’s reproducible directly.
null instead of missing. A cURL command with the actual body would have shown that in the first message.The API Bug Report Template
| Field | What to write |
|---|---|
| Title | [METHOD] [endpoint] returns [actual] instead of [expected] when [condition] |
| Environment | QA / staging URL, build or version, date and time with time zone |
| Preconditions | User and role, test data ids, feature flags |
| Steps / Request | cURL command (or Postman request) that reproduces it |
| Actual result | Status code, relevant headers, response body (trimmed) |
| Expected result | Exact status and body, with the reference (spec section, story, acceptance criteria) |
| Evidence | Request/trace id, timestamp, screenshots of logs, HAR or Postman console export |
| Reproducibility | Always / 3 of 10 / only with specific data |
| Severity / Priority | Impact on system / urgency to fix |
| Impact | Who is affected and how (users, money, data, security) |
| Notes | Workaround, first seen in build X, related tickets, your analysis |
Writing a Great Title
| Weak title | Strong title |
|---|---|
| Validation not working | POST /users accepts age 17 (min is 18) and returns 201 |
| Error in order API | POST /orders returns 500 instead of 422 when quantity is negative |
| Security issue | GET /orders/{id} returns another customer’s order (BOLA) |
| Wrong status | DELETE /cart/items/{id} returns 200 with empty body; spec says 204 |
| Slow API | GET /products?category=books p95 latency 4.2 s (SLA 800 ms) on QA |
A strong title lets a lead triage the bug without opening it.
Capturing the Request Correctly
- Postman: Code (
</>) → cURL. Make sure variables are resolved (check the Console). - Browser: DevTools → Network → right-click → Copy as cURL. Or save a HAR file for multi-request issues.
- Playwright: attach request and response to the test report, or use the trace viewer.
- Mobile: proxy the app through Charles, Proxyman or mitmproxy on a test device.
<ADMIN_TOKEN>. Tickets are read by many people and often synced to other tools. Never paste production customer data.Example 1: Validation Bug (Full Report)
Title: POST /objects returns 500 when "ttl" is a JSON array instead of 422
Environment: QA - https://qa.shop.example/api/v1, build 2026.11.3, 12-Nov-2026 15:20 IST
Preconditions: none (endpoint is public)
Request:
curl -i -X POST "https://qa.shop.example/api/v1/objects" \
-H "Content-Type: application/json" \
-d '{"name":"Bag","ttl":[60]}'
Actual:
HTTP/1.1 500 Internal Server Error
X-Request-Id: 9f2c1d7e
{"error":"Unhandled exception"}
Expected:
HTTP/1.1 422 with error.details[0].field = "ttl"
(OpenAPI: NewObject.ttl is integer 10-1800; API guidelines section 4: invalid input -> 422)
Reproducibility: 5/5. Same result for ttl = {}, true.
Severity: Medium (unhandled exception; no data impact) Priority: P2
Impact: Any client sending a wrong type gets a 500; error monitoring is flooded.
Notes: ttl = "60" (string) correctly returns 422, so only non-scalar types fail.
Notice the extra analysis in Notes. It narrows the cause to “non-scalar types” and saves the developer time.
Example 2: Security Bug (BOLA)
Title: GET /orders/{id} returns other customers' orders (BOLA / IDOR)
Environment: Staging, build 2026.11.2
Preconditions: Customer A (qa_cust_a) and Customer B (qa_cust_b), both role=customer.
Order 70412 belongs to B.
Request (as A):
curl -i "https://staging.shop.example/api/v1/orders/70412" -H "Authorization: Bearer <TOKEN_CUSTOMER_A>"
Actual: 200 OK with B's name, phone, address and items.
Expected: 404 (or 403) and no data. (Security requirement SEC-12: users access only their own orders.)
Reproducibility: Always. Order ids are sequential, so any order can be read.
Severity: Critical Priority: P1 (blocker for release)
Impact: Personal data of all customers exposed to any logged-in user. Data protection risk.
Notes: PATCH /orders/70412 as A returns 403 correctly, so only the read path lacks the ownership check.
Security bugs often go into a restricted ticket or a separate security queue. Follow your company’s disclosure process.
Example 3: Real Findings From This Course
While writing these lessons we tested real public services. Two genuine, reportable observations:
Stack trace in a public SOAP service
Title: Divide with intB=0 returns SOAP Fault exposing stack trace and server file path
Service: http://www.dneonline.com/calculator.asmx (public demo calculator)
Actual: HTTP 500, faultcode soap:Server, faultstring contains
"System.OverflowException ... at Calculator.Divide(Int32 intA, Int32 intB) in G:\PleskVhosts\...\Calculator.asmx:line 19"
plus header X-AspNet-Version: 2.0.50727
Expected: soap:Client fault with a safe message such as "intB must not be 0"; no stack trace, paths or version headers.
Severity: Medium (information disclosure, OWASP API8)
Inconsistent error format for unknown versions
The SQA Tools practice API returns its own error shape {"error":{"status","code","message"}} everywhere, except for unknown routes such as a wrong version, where the underlying framework answers with a different shape:
https://sqatools.in/wp-json/sqa-api/v1/../v9/userscurl -i \
"https://sqatools.in/wp-json/sqa-api/v1/../v9/users"GET https://sqatools.in/wp-json/sqa-api/v9/users
Actual: 404 {"code":"rest_no_route","message":"No route was found ...","data":{"status":404}}
Expected: 404 in the documented format {"error":{"status":404,"code":"not_found","message":"..."}}
Severity: Low Priority: P3
Impact: Clients that parse error.code break on this path.
Low severity, but exactly the kind of consistency issue a careful tester reports.
Severity vs Priority
| Severity | Priority | |
|---|---|---|
| Meaning | How bad the impact on the system or users is | How soon it must be fixed |
| Set by | Usually the tester | Usually product owner / lead |
| Levels | Critical, High (Major), Medium, Low | P1 (now), P2, P3, P4 |
| API example | Severity | Priority |
|---|---|---|
| Payment API double-charges on retry | Critical | P1 |
| BOLA exposing customer addresses | Critical | P1 |
| Admin report export returns 500 for one rare filter | High | P3 (used once a month, workaround exists) |
| Typo in error message shown to all users at launch | Low | P2 (visible, cheap to fix) |
| Error format inconsistent on unknown routes | Low | P3 |
Before You Click “Create”: Self-Check
- Did I reproduce it at least twice, ideally with a fresh token and fresh data?
- Is it really a bug? Check the spec, the story and recent changes. If the spec is unclear, raise a question rather than a bug.
- Is it a duplicate? Search existing tickets by endpoint.
- Did I isolate the minimum request that fails (remove unrelated fields)?
- Did I include request id/timestamp so logs can be found?
- Did I remove secrets and personal data?
- Is the expected result backed by a reference, not my opinion?
Automation Failures vs Real Bugs
When an automated API test fails, triage before reporting:
- Product bug: the API changed behaviour against the spec. Report with the request from the test report.
- Test bug: wrong expectation, hardcoded data, order dependency. Fix the test.
- Environment issue: service down, expired test credentials, data reset. Notify the environment owner, don’t log a product bug.
- Flaky: passes on rerun. Investigate timing, shared data and rate limits; don’t just add retries.
Playwright makes evidence easy: attach the response to the report on failure.
test('POST /objects returns 201', async ({ request }, testInfo) => {
const res = await request.post(`${BASE}/objects`, { data: { name: 'Bag', ttl: 60 } });
if (res.status() !== 201) {
await testInfo.attach('response', {
body: JSON.stringify({ status: res.status(), headers: res.headers(), body: await res.text() }, null, 2),
contentType: 'application/json',
});
}
expect(res.status()).toBe(201);
});
Communicating With Developers
- Be factual: “returns 500” not “the API is broken”.
- Offer analysis, not blame: “fails only for non-scalar ttl values” helps more than “validation is missing everywhere”.
- For critical bugs, message the developer or lead directly as well as filing the ticket.
- Retest promptly when fixed, on the same data plus related cases (fixes often break neighbours).
- Close the loop: add an automated regression test so it can’t come back.
Interview Questions: API Bug Reporting
What do you include in an API bug report?
A specific title, environment and build, preconditions, the exact request as cURL, the actual status, headers and body, the expected result with a reference, request id and timestamp, reproducibility, severity, priority, impact and any analysis or workaround, with secrets removed.
What is the difference between severity and priority?
Severity is the degree of impact of the defect on the system or users; priority is the urgency of fixing it based on business needs. A high-severity bug can have low priority and vice versa.
Give an example of high severity and low priority.
A crash (500) in a rarely used admin export with an available workaround: technically severe but can wait for a later release.
Give an example of low severity and high priority.
A wrong brand name or spelling in an error message shown to every user on launch day: little functional impact, but needs fixing immediately.
Why include a cURL command in an API bug?
It reproduces the exact request, including method, headers and body, on any machine in seconds, removing ambiguity and “works on my machine” delays.
Why include a request id or timestamp?
It lets developers find the exact request in server logs and distributed traces across services, speeding up root-cause analysis.
What would you do if the developer says it’s not a bug?
Check the reference (spec, story, acceptance criteria) and share it. If requirements are unclear, involve the product owner to decide, and update the spec or tests based on the decision.
How do you decide if an automated test failure is a real bug?
Rerun and analyse: check whether the API behaviour changed against the spec, whether the test or data is wrong, whether the environment is unhealthy, or whether it’s flaky due to timing or shared data. Only product behaviour issues become bug reports.
How do you report a security vulnerability found in an API?
Follow the company’s security process: restricted ticket or security channel, minimal reproduction with test accounts, clear impact, no public sharing and no exploitation beyond proving the issue.
What is a good bug title format for APIs?
[METHOD] [endpoint] returns [actual result] instead of [expected result] when [condition], for example “POST /orders returns 500 instead of 422 when quantity is negative”.