An API security checklist is the set of tests an API has to pass before it is exposed to real users: every route has a known caller, an authorization rule, validated input, bounded resource use, safe output, and a monitored failure path. The ten API security testing steps below turn those requirements into requests you can run against the deployed API, with the browser steps to run each one without reading the code.
This API security checklist is ordered by the trust boundaries that can expose another customer’s data, grant privileged work, or create an unbounded bill. The frequency column comes from a fixed cohort of 21 third-party AI-built apps reviewed in June and July 2026. It identifies recurring test targets and does not estimate failure rates for all AI-built apps or all APIs. The standard underneath is the OWASP API Security Top 10, 2023 edition, which is still the current edition as of August 2026, and the last section maps every check back to it.
API security checklist: the ten checks
| Priority | Check and evidence | Signal in the fixed cohort |
|---|---|---|
| 1 | Test object authorization with two accounts on reads, writes, deletes, files, and functions | Cross-user access confirmed in 7 of 21; RLS gaps in 9 of 21 |
| 2 | Require authentication and role authorization on every privileged route | Unauthenticated privileged work in 11 of 21 |
| 3 | Recompute security-sensitive values on the server | Client-trusts-itself paths in 10 of 21 |
| 4 | Bound costly and high-volume workflows before work begins | No rate limit on the most expensive endpoint in 13 of 21; denial-of-wallet in 12 of 14 apps with an AI surface |
| 5 | Validate input shape, type, size, content, and file properties | No stable corpus-wide denominator; required by the route’s data boundary |
| 6 | Keep privileged secrets server-side and rotate exposed values | Real secrets committed or shipped in 6 of 21 |
| 7 | Harden CORS, errors, methods, content types, and production configuration | No stable corpus-wide denominator |
| 8 | Inventory every API version, host, preview, webhook, and maintenance route | No stable corpus-wide denominator |
| 9 | Treat data returned by third-party APIs as untrusted input | No stable corpus-wide denominator |
| 10 | Log security events and alert on consequential failures | No error tracking or alerting in 17 of 21 |
The counts are deliberately separated where eligibility differs. The denial-of-wallet denominator covers 14 third-party apps with an AI or LLM surface. The other measured rows cover the 21-app third-party cohort. Unmeasured rows stay unmeasured.
If you only have an evening, run checks 1 and 6: two accounts against your own API, then a search of the shipped bundle and Git history for keys. Checks 4 and 10 want a staging copy or a quiet window, because they generate load and fire real alerts. A small API with one database and one payment provider takes a focused day; several hosts, versions, and integrations take two to three.
The whole list, in one place
- 01 Test object authorization with two accounts on every read, write, delete, file, and function
- 02 Require authentication and a role check on every privileged route
- 03 Recompute prices, roles, ownership, and usage counts on the server
- 04 Bound and throttle every costly workflow before the work starts
- 05 Validate input shape, type, size, content, and file properties
- 06 Keep privileged secrets server-side and rotate anything exposed
- 07 Harden CORS, security headers, TLS, methods, and production errors
- 08 Inventory every version, host, preview, webhook, and maintenance route
- 09 Treat third-party responses and fetched URLs as untrusted input
- 10 Log security events and alert on the failures that matter
A checklist item with no denominator behind it is a guess wearing a bullet point.
How to run these tests without reading the code
You do not need the source to run an API security test. You need two accounts and the Network tab.
Open your app in a normal browser window and sign in as account A. Open a second browser profile, or an incognito window, and sign in as account B. In account A’s window, open developer tools, switch to the Network tab, and use the feature you want to test. Each request the app sends appears in the list. Right-click the one that matters and choose Copy as cURL. Paste it into a terminal and it runs exactly as the browser sent it, headers and all. Postman will import the same cURL string if you prefer a window to a shell.
Now change one thing at a time. Swap account A’s Authorization header or session cookie for account B’s and resend. Change the object ID in the URL to one that belongs to account A and resend as B. Delete the auth header entirely and resend.
Read the result the same way every time:
- Pass: a 401 or 403, or a 200 with an empty list, and a generic body that names no other account.
- Fail: a 200 carrying the other account’s JSON, or any 500 whose body leaks a query, a file path, or a stack trace.
Record four things per test: the URL, the method, the status code, and the first line of the response body. That record is the evidence, and it is what makes a fix verifiable later.
1. Test object-level authorization with two accounts
OWASP API1:2023 Broken Object Level Authorization applies when an endpoint receives an object identifier and fails to verify that the caller may act on that object. Authentication can work perfectly while GET /api/orders/123 returns another customer’s order. Generated schemas usually hand out sequential integer IDs, so an attacker does not have to guess anything: 123 becomes 124. Random UUIDs make enumeration harder, but they are obscurity, not authorization. Both still need the check below.
How to test for BOLA with two accounts
Create account A and account B with different records. Capture account A’s real requests, authenticate as account B, substitute account A’s object IDs, and repeat each operation:
- read one object and list objects;
- create a child object under another user’s parent;
- update and delete;
- download and upload files;
- call RPCs, server functions, GraphQL resolvers, and batch endpoints.
What a failed object-authorization response looks like
Expect an authorization error or no returned object. A 200 that returns account A’s record to account B is the failure, and it is the same shape whether the data came from an ORM, a REST route, or a database policy. Repeat with a guessed ID, a valid ID from another tenant, and any organization or workspace identifier the client can change. On Supabase projects this is the exact boundary that decides whether someone can steal data from your app. The OWASP Authorization Cheat Sheet recommends validating permissions on every request and testing authorization logic with unit and integration tests.
2. Protect every privileged function
OWASP API5:2023 Broken Function Level Authorization covers ordinary users reaching administrative or higher-privilege functions.
Privileged routes AI builders leave behind
Generated projects often retain setup, migration, test-data, resend, export, impersonation, and cleanup routes after the interface stops showing them.
Build an inventory from the deployed router, server functions, edge functions, RPCs, webhooks, and infrastructure configuration. For each privileged action, test logged-out, ordinary-user, support, and admin roles. Changing an HTTP method, path version, content type, or hidden parameter should not bypass the same authorization decision.
OWASP API2:2023 Broken Authentication adds the identity boundary. Most generated backends hand you a JWT from the auth provider, so test the token itself: missing, malformed, expired, revoked, tampered, and wrong-audience JWTs; logout and password-reset invalidation; API-key scope; account lockout; and authentication on sensitive flows that use a separate host or protocol. The server should verify the token signature and derive the actor before authorization begins. Do not use basic auth on a privileged route: it ships a reusable password on every request and gives you no way to revoke one session.
The worst version I’ve personally traced sat in a multi-tenant retail POS app, a v0-origin build with real customer inventory and sales data. A GET route, meant as a maintenance shortcut, dropped every tenant’s tables. No auth, no rate limit, and its only guard was a secret passed as a URL query parameter, which put that same secret in the server’s own access logs and in anyone’s browser history the moment they visited the URL once.
A secret in a query string is a poor substitute for identity and permission. URLs can enter browser history, access logs, referrer data, screenshots, support tickets, and analytics. Use the application’s identity provider, a narrowly scoped service identity, or a verified webhook signature as appropriate for the actor.
3. Recompute trusted values on the server
The browser can change every request it sends. Prices, discounts, account IDs, roles, plan names, feature flags, usage counts, webhook states, and ownership fields require a server-side source of truth.
OWASP API3:2023 Broken Object Property Level Authorization covers reading or changing object properties the caller should not control. Test by adding fields the interface never sends, changing read-only fields, and removing fields the server expects. Use an allowlist of writable properties for each role and operation.
For payments, retrieve or map the product and price on the server and grant access from a verified provider event. For membership, derive the caller from the verified session and check the target organization server-side. Checkout trust-boundary failures use the same mechanism as a user assigning their own role.
Where AI builders leave the API open by default
The first three checks fail in the same handful of places on generated apps. The backend and its default controls differ by platform.
The wrong Supabase key in the browser. Supabase ships two kinds of key. The publishable (anon) key is safe to expose online, including in web pages, mobile apps, and source code. The secret (service_role) key carries the Postgres BYPASSRLS attribute, so it skips every Row Level Security policy you wrote, and Supabase’s own instruction is never to use it in a browser, even on localhost. A generated project that pasted the secret key into client code to make a query work has no authorization boundary left to test.
RLS on with no policies, or off entirely. Enabling RLS on a table without writing policies denies everything, which is loud and gets noticed. The dangerous state is the reverse: a table left unprotected while the app talks to it with an exposed key. Run the two-account test through the Data API, not through your app’s UI. How to test Supabase RLS walks the queries, and whether RLS alone is enough covers what it does not reach.
Firebase left in test mode. Cloud Firestore’s test-mode rules are, in Google’s words, good for getting started but they allow anyone to read and overwrite your data. Locked mode denies all access instead. Check which one the project is on before launch, not after.
Generated functions with no auth guard. Edge functions, server actions, and API routes an AI wrote to unblock a feature usually trust their caller. Read the first five lines of each one: if nothing derives the user before the work happens, it is check 2’s problem.
Preview deployments on the production database. A preview URL with production credentials is a second, unhardened front door to the same data. Give previews their own database, or keep them behind authentication.
4. Bound resource consumption and sensitive workflows
OWASP API4:2023 Unrestricted Resource Consumption includes missing limits on request rate, payload size, records returned, execution time, file size, external-provider spend, and other resources.
Apply limits before the costly operation begins. Rate limiting and throttling are the same control at two speeds: reject the excess request, or slow it down. Choose keys that match the threat and product: verified user, organization, API client, IP range, or a combination. A global requests-per-minute limit alone may allow one account to exhaust a shared AI, email, SMS, export, or payment quota.
How to rate limit an AI or LLM endpoint
The fix is middleware on the specific route, not a global default you forget exists. Here’s a runnable example against express-rate-limit v8, the current release as documented in its own repository:
import { rateLimit, MINUTE } from 'express-rate-limit'
const expensiveLimiter = rateLimit({
windowMs: 15 * MINUTE,
limit: 100,
standardHeaders: 'draft-8',
legacyHeaders: false,
})
app.post('/api/generate', expensiveLimiter, generateHandler)
Mount the limiter on the one or two routes that actually cost you, not the whole app.
An AI endpoint needs two more controls the request counter does not give you. First, a per-user token cap: a caller who never exceeds 100 requests can still burn a month of budget if each request carries a 200,000-token document, so meter tokens and spend per account and per day, not just calls. Second, an identity check on the proxy route itself. The most expensive route in a generated app is often a thin /api/chat or /api/generate that forwards to the model provider with your key attached, and it is frequently reachable without an account because the interface never showed a logged-out state. Open it in a private window with no session and send one request. If you get a completion back, anyone with the URL is spending your money.
Limits on sensitive business flows
OWASP API6:2023 Unrestricted Access to Sensitive Business Flows covers workflows whose legitimate automation can harm the business at scale, such as mass signup, reservation, purchase, referral, comment, or account-recovery activity. Add workflow-specific limits and abuse signals as well as route-level throttling.
Test these cases against the deployed API:
- repeat the request until the documented limit is reached;
- retry in parallel to look for a race around the counter;
- vary IP addresses while keeping the same account;
- vary accounts while targeting the same organization or expensive resource;
- send the largest accepted payload and request the largest result set;
- confirm rejected work does not call the paid provider first.
Record the response, retry guidance, counter reset, alert, and provider-side effect. Capacity failures around 100 concurrent users often share the same missing bounds even when abuse is not the trigger.
5. Validate input before it reaches a sensitive operation
Define an explicit schema for path parameters, query parameters, headers, and bodies. Validate type, length, range, format, allowed values, nesting depth, and required combinations. Reject unknown properties where the client has no reason to send them.
The OWASP Input Validation Cheat Sheet recommends allowlist validation and early rejection at the application’s trust boundary. Parameterized queries remain necessary after schema validation because a syntactically valid string can still contain hostile content.
File uploads need checks for size, extension, detected content type, storage name, destination, and who can retrieve the file. Archive extraction needs limits on expanded size and paths. URLs supplied for server-side fetching need the SSRF controls in check 9.
Test missing fields, duplicate fields, wrong types, boundary sizes, deeply nested input, extra properties, malformed encodings, and content that is valid syntax with unsafe meaning. Confirm the response is generic enough for the caller and detailed enough in internal logs.
6. Keep privileged secrets out of client and repository history
Search the current tree, Git history, build output, source maps, browser responses, logs, prompts, CI artifacts, and deployment configuration for credentials. The OWASP Secrets Management Cheat Sheet covers central storage, least privilege, rotation, revocation, auditing, and the full secret lifecycle.
Classify each value before treating it as a leak. Public client identifiers and deliberately public anonymous keys can appear in browser code. Database-admin credentials, service-role keys, private API keys, signing secrets, and provider credentials belong on a trusted server boundary.
Rotate any privileged credential that crossed that boundary. Removing the current line does not invalidate a value already cloned, cached, logged, or copied. Verify the old credential fails and review its provider logs for use during the exposure window.
7. Harden production configuration and responses
OWASP API8:2023 Security Misconfiguration includes insecure defaults, unnecessary methods or features, missing TLS, permissive cross-origin rules, verbose errors, and inconsistent hardening across the stack.
Review the deployed environment rather than development defaults:
- serve the API over TLS 1.2 or later, redirect or reject plaintext access, and send
Strict-Transport-Security(HSTS); - allow only required HTTP methods and content types;
- set an explicit CORS allowlist for browser clients and handle credentials deliberately;
- send
X-Content-Type-Options: nosniffso a browser cannot reinterpret a response as a different type; - remove debug pages, stack traces, framework fingerprints, test routes, and directory listings;
- use generic client errors with a request identifier that maps to protected internal detail;
- verify cache rules do not store private responses for another user;
- patch reachable vulnerable components and remove unused services.
CORS, headers, and what they do not do
CORS controls which browser origins may read a response. It does not authorize the user or repair a direct API request. Security headers also complement authorization and input handling; they cannot replace them.
The same limit applies to the products sold as the answer. An API gateway or a web application firewall inspects traffic patterns: rates, signatures, payload shapes, known-bad clients. Neither one knows which rows belong to which customer, or what a discount should have cost. A gateway cannot pass check 1 or check 3 for you, because the request that reads another customer’s order is a perfectly well-formed, authenticated request. Buy them for the jobs they do, and still run the two-account test.
8. Maintain a deployed API inventory
OWASP API9:2023 Improper Inventory Management covers forgotten hosts, versions, environments, and documentation gaps. These have names worth knowing: a shadow API is a live endpoint nobody documented, and a zombie API is an old version or deployment still answering after it was supposed to be retired. Include production, previews, staging, regional hosts, old API versions, mobile backends, GraphQL endpoints, server functions, webhooks, and temporary migration routes.
For each entry, record owner, purpose, environment, data classification, authentication method, public hostname, version, deployment source, last activity, and retirement date. Compare the inventory with DNS, hosting projects, gateways, source repositories, function dashboards, and observed traffic.
Retirement needs verification. Remove DNS and routing, disable credentials, archive required evidence, and confirm traffic no longer reaches the old deployment. An undocumented preview with production credentials can preserve a fixed vulnerability after the main hostname is patched.
9. Treat third-party API responses and fetched URLs as untrusted
OWASP API10:2023 Unsafe Consumption of APIs warns that developers may trust data from integrated APIs more than user input. Validate provider responses before using them in authorization, payment state, redirects, database writes, or rendered output. Enforce timeouts, response-size limits, safe retry rules, and idempotency for side effects.
When the API fetches a user-supplied URL, apply the OWASP SSRF Prevention Cheat Sheet. Parse and validate the destination, restrict schemes and ports, resolve and check addresses, block private and metadata ranges, and revalidate redirects. Network egress controls provide another boundary when the runtime supports them.
If any of that fetched content reaches a model, the untrusted-input rule gets sharper. Prompt injection is third-party data that carries instructions: a scraped page, a support ticket, a PDF, or a provider’s JSON field that tells your model to ignore its system prompt and call a tool. Treat model output derived from fetched content as untrusted too. Never let it decide an authorization outcome, a payment state, or which database row to write, and give any tool the model can call the same permission checks a human caller would face.
Test provider errors, malformed responses, duplicate webhooks, delayed events, timeouts, partial success, and redirects to a prohibited address. Confirm retries cannot duplicate a charge, entitlement, email, or destructive write.
10. Log security events and alert on consequential failures
Logs should answer who performed an action, what object or function was targeted, when it happened, which outcome occurred, and which request ties the events together. Avoid recording passwords, session tokens, private keys, full payment data, health details, or other sensitive payloads without a defined need and protection.
The OWASP Logging Cheat Sheet recommends application-level security logging, consistent event fields, protected log transport and storage, monitoring, and testing of logging failures. Record authentication failures, authorization denials, privilege changes, credential changes, input-validation failures, rate-limit events, administrative actions, and high-impact workflow outcomes.
Trigger one known security event in a safe environment. Confirm the log contains the intended fields, the alert reaches its owner, the request can be traced across services, and a logging outage does not crash the API or expose sensitive detail.
How the checklist maps to the OWASP API Security Top 10
The current OWASP API Security Top 10 is the 2023 edition. It is a risk catalog rather than a complete implementation procedure, which is why most API security best practices lists stop at naming the control. This checklist adds concrete tests and the logging control that the fixed cohort showed was frequently absent. Logging had its own slot in the 2019 edition, as API10:2019 Insufficient Logging & Monitoring, and was folded out of the 2023 list, so the single most common failure in the cohort no longer appears in the current official list at all.
| OWASP API risk | Checklist coverage |
|---|---|
| API1 Broken Object Level Authorization | Two-account object tests in check 1 |
| API2 Broken Authentication | Identity, session, token, and actor checks across checks 1 and 2 |
| API3 Broken Object Property Level Authorization | Writable-property allowlists and server-derived values in check 3 |
| API4 Unrestricted Resource Consumption | Rate, size, concurrency, cost, and quota tests in check 4 |
| API5 Broken Function Level Authorization | Role tests for privileged functions in check 2 |
| API6 Unrestricted Access to Sensitive Business Flows | Payment, entitlement, signup, export, and abuse controls across checks 3 and 4 |
| API7 Server Side Request Forgery | Destination and egress controls in check 9 |
| API8 Security Misconfiguration | Production configuration and response hardening in check 7 |
| API9 Improper Inventory Management | Host, version, preview, route, and retirement inventory in check 8 |
| API10 Unsafe Consumption of APIs | Provider-response, timeout, retry, redirect, and idempotency tests in check 9 |
Common questions about API security checklists
What is an API security checklist?
An API security checklist is the set of tests an API has to pass before it is exposed to real users. It covers a known caller and an authorization rule on every route, validated input, bounded resource use, safe output, and a monitored failure path. A useful one names the test to run, not just the control to have.
What is the minimum API security checklist before launch?
Inventory the deployed routes, test object and function authorization with two roles, validate every input schema, limit expensive operations, search and rotate exposed secrets, harden production errors and CORS, verify third-party failure handling, and trigger a security event that reaches a monitored log. Scope the depth to the data and consequences of the API.
What is BOLA, and how do I test it?
Broken object-level authorization occurs when an API accepts an object ID and fails to verify that the authenticated caller may access that object. Create two accounts, capture one account’s request, repeat it as the other account with the first account’s ID, and expect denial or no data for reads and writes.
How do I test if my API is secure without writing code?
Sign in as one account in your normal browser and as a second account in another profile, then open developer tools and use the app while watching the Network tab. Right-click any request, choose Copy as cURL, and replay it in a terminal with the other account’s auth header or with no auth header at all. A 401, a 403, or an empty result is a pass; a 200 carrying the other account’s data is a fail. Record the URL, status, and first line of the body for each test.
Does an API gateway or WAF cover this checklist?
No. A gateway or web application firewall filters traffic patterns, rate spikes, and known-bad payloads, which is useful but separate. Neither one knows which rows belong to which customer or what a price should be, so neither can pass the object-authorization or server-recomputation checks. You still have to test those against your own routes.
Is the security my AI builder ships by default enough?
Not on its own. Builders like Lovable, Bolt, Base44, Replit, and Cursor generate working code fast, and the defaults that make a feature work are often the ones that leave the boundary open: a secret key used client-side, a table with Row Level Security off, Firebase left in test mode, or a generated function with no auth guard. The platform secures its own infrastructure; the authorization rules for your data are yours to write and yours to test.
What are shadow APIs and zombie APIs?
A shadow API is a live endpoint nobody documented or reviewed, such as a preview deployment, an internal debug route, or a function an AI added to unblock a feature. A zombie API is an old version or environment still answering requests after it was meant to be retired. Both matter because they usually miss the fixes applied to the main hostname.
Is passing the OWASP API Top 10 enough?
The OWASP list organizes common risk classes, and broader frameworks such as the NIST Cybersecurity Framework organize the program around them. A production decision still needs tests of the deployed routes, business rules, failure paths, provider behavior, logging, recovery, and any legal or privacy requirements specific to the application.
How often should the API checklist be rerun?
Rerun affected checks after changes to identity, roles, routes, database policies, payment or AI workflows, dependencies, hosting, CORS, webhooks, or third-party integrations. Run the core deployed checks before a material launch and keep authorization, input, secrets, dependency, and logging tests in the normal release gate.
When every fix and release still depends on you
AxonBuild can trace the failure, repair the broken workflow, and ship the next change without rebuilding the parts that already work.