API security

API Security Best Practices

Secure APIs with layered controls: strong identity, object-level authorization, strict input handling, abuse prevention, limited data exposure, and observable enforcement close to application logic.

14 min read
In short: Secure APIs with layered controls: strong identity, object-level authorization, strict input handling, abuse prevention, limited data exposure, and observable enforcement close to application logic.

What is API security?

API security protects an interface from unauthorized access, data exposure, malicious input, and automated abuse. Effective protection covers the full lifecycle: design, implementation, deployment, operation, versioning, and retirement.

The key principle is simple: every request must prove its identity when necessary, be allowed to perform the specific action on the specific object, satisfy the API contract, stay within resource limits, and leave enough evidence for investigation. No gateway, token format, or scanner can provide all five guarantees by itself. A public catalog and a funds-transfer endpoint therefore need different control strengths.

API security checklist

Use this five-layer baseline during design reviews and release checks. Each layer addresses a different failure mode, so passing one does not compensate for skipping another.

LayerBaseline controlsQuestions to verify
Identity and access

Authenticate non-public operations; authorize every object and function; use scoped, revocable credentials

Can this caller perform this action on this tenant's object right now?

Contract and data

Validate paths, queries, headers, cookies, and bodies; constrain responses; validate downstream data

Are the shape, meaning, size, and destination of every value acceptable?

Resource and abuse

Bound rates, concurrency, payloads, execution time, pagination, and costly business actions

What does one request consume, and what happens when valid requests are automated?

Lifecycle and exposure

Inventory APIs, owners, versions, dependencies, and data classes; retire old versions and secrets

Is every reachable interface intentional, supported, and monitored?

Detection and assurance

Log security decisions; alert on meaningful behavior changes; continuously test negative cases

Can the team detect, investigate, contain, and prevent a repeat of misuse?

Keep secrets out of URLs, logs, analytics, client bundles, and error messages across every layer.

The OWASP API Security Top 10 is a useful review framework. It covers broken object and function authorization, unrestricted resource consumption, server-side request forgery, unsafe consumption of APIs, and improper inventory management. It is a threat model, not a substitute for an interface-specific assessment.

Use strong authentication

Authentication should establish which human, service, or device is calling and how confidently the service knows that identity. Prefer short-lived, narrowly scoped credentials over permanent shared secrets. API keys can identify a calling application, but they usually do not establish the end user's identity and should not be treated as user sessions.

For OAuth, use a flow intended for the client type and deployment model. Authorization Code with PKCE protects public clients from intercepted authorization codes. Client credentials are for a confidential client acting on its own behalf, or under an authorization previously arranged with the authorization server; they do not represent delegated user authority. Redirect URIs must be matched strictly, and authorization codes must be short-lived and single-use. The current OAuth security recommendations are specified in RFC 9700.

Credential validation must happen before claims influence authorization. For a signed token, verify the cryptographic signature using an explicitly allowed algorithm and trusted key source. Then verify issuer, audience, expiry, not-before time where used, token type, and any required scope. Do not select an algorithm merely because the token header requests it, and do not accept an access token issued for another service. RFC 8725 describes defensive JSON Web Token validation.

Key rotation needs overlap: publish a new verification key before using it, retain the old key until its tokens expire, and remove compromised keys through an incident procedure. Opaque-token introspection enables faster revocation but adds latency and a network dependency. API keys need scoped permissions, expiry where practical, usage records, and self-service revocation. Store a one-way representation when the original key need not be displayed again, and allow two keys briefly during rotation.

Authentication is not authorization. A valid token proves an identity under specific conditions; it does not prove that identity may read an invoice, modify another user's project, or call an administrative function.

Agentic applications must also distinguish the controlling user from the software actor. The on-behalf-of authorization guide explains delegated user and agent identity, OAuth token exchange, actor chains, and why delegated authority does not prove intent.

Enforce authorization per object

Authorize at the point where the application knows the authenticated principal, tenant, requested object, operation, and current object state. A broad route check such as “user may access projects” is insufficient if the database lookup can return a project from another tenant.

Object-level authorization should constrain retrieval itself when possible. For example, look up an invoice by both its ID and the caller's tenant, then apply role and state checks before returning it. An opaque or random ID reduces easy enumeration but remains untrusted input, not proof of access.

Function-level authorization protects operations rather than records. A support agent might read an account but not change its billing owner; a project editor might update content but not invite an administrator. Enforce these distinctions server-side even when the user interface hides unavailable controls. Deny by default when a new role, action, or object state has no explicit policy.

Test horizontal escalation, where one customer accesses another customer's object, and vertical escalation, where a normal user reaches an administrator function. Also test indirect references: child resources, export jobs, file downloads, and search results must preserve the parent authorization boundary. OWASP describes these risks as Broken Object Level Authorization and broken function-level authorization.

Validate every input

What should an API request schema validate?

Validate more than JSON bodies. Paths, query parameters, headers, cookies, multipart filenames, compressed content, and values returned by downstream services are all untrusted. An explicit schema should define types, formats, enumerations, string lengths, numeric ranges, array counts, nesting depth, payload size, and whether unknown fields are accepted.

Rejecting unknown fields catches misspellings and prevents hidden properties from reaching persistence. Accepting them can improve forward compatibility during staged client and server releases. Choose deliberately per version; do not silently persist fields the current service does not understand.

How should an API validate business rules and destinations?

Syntactic validity is only the first step. A date can be well formed but outside the permitted booking window, and two valid values can form an invalid range. Validate business invariants after parsing. Normalize once so gateways and applications interpret case, Unicode, URL encoding, duplicate keys, and paths consistently; reject ambiguous encodings.

Use parameterized database queries and context-appropriate output encoding. For outbound HTTP requests, allow only required schemes and hosts, check resolved addresses, and control redirects. A hostname allowlist alone is insufficient if DNS or redirects lead to private infrastructure. Network isolation adds protection but complicates dynamic integrations.

Why validate downstream responses and webhooks?

Validate downstream responses against the assumptions the caller relies on. A partner API returning an unexpected enum, oversized collection, active HTML, or a tenant identifier different from the request should fail safely rather than flow directly into a database or response. Apply timeouts, response-size limits, TLS verification, and explicit error handling. Trust boundaries do not disappear because the other service is internal.

Webhooks require separate inbound controls. Verify the provider's signature over the exact raw request bytes before parsing or transforming the body, use a constant-time comparison where applicable, and keep signing secrets separate by environment and endpoint. Reject stale timestamps within a documented tolerance and store an event ID or delivery ID so retries are idempotent. A timestamp limits the replay window; it does not prevent two deliveries inside that window. Deduplication state prevents reprocessing but needs retention, cleanup, and clear behavior if the store is unavailable.

Add rate limits and bot protection

Valid requests can still cause harm at scale. Bound the resource actually being consumed: requests, concurrent jobs, login attempts, password resets, emails sent, records scanned, files decompressed, exports created, or AI tokens. Add maximum payload and response sizes, pagination caps, query complexity limits, execution deadlines, and cancellation of downstream work after a client disconnects.

Choose identifiers that match the threat and fairness goal. Account or user limits survive ordinary IP changes and are appropriate for authenticated quotas. IP limits help before login, but shared networks can cause false positives and attackers can rotate addresses. Device, session, organization, and operation-specific limits can complement both.

Fixed-window counters are simple but permit bursts at boundaries. Sliding windows are smoother but cost more state. Token buckets permit controlled bursts while enforcing a sustained rate. Concurrency limits protect scarce workers better than request-per-minute limits when operations have very different durations. The rate limiting guide compares these approaches in more detail.

Bot detection is probabilistic. Combine network and client signals with identity age, route, session history, and action cost. When false positives matter, prefer step-up verification, delayed processing, reduced quotas, or review queues over immediate blocking. Evaluate uncertain controls in dry-run mode before enforcement.

Business-logic abuse often uses fully valid sequences: creating many trial accounts, reserving scarce inventory, scraping through search, or repeatedly triggering expensive previews. The API abuse guide explains why behavioral and outcome-based controls are needed alongside request validation.

Lifecycle controls are also exposure controls. Maintain a machine-readable inventory of public, partner, internal, callback, and administrative APIs, including hostname, route or schema, owner, data classification, authentication method, deployed versions, dependencies, and retirement date. Compare that inventory with gateway, DNS, load balancer, and service-discovery data to find shadow or forgotten endpoints.

Version retirement should have an announced timeline, usage telemetry by client, migration guidance, and an escalation path for remaining consumers. Stop issuing credentials for the old version, disable it in a staged manner, then remove routes, documentation, secrets, firewall rules, and monitoring exceptions. Keeping an old version indefinitely avoids short-term migration work but preserves vulnerable code and expands the test matrix.

Minimize data exposure

Return only the fields and records required for the operation. Use response schemas rather than serializing database models directly. Enforce pagination and upper bounds on bulk exports, and authorize each included record. Field-level filtering is especially important when the same object has public, customer, support, and administrator views.

Classify sensitive fields so logs, traces, analytics, caches, and support tools consistently redact them. Redaction at ingestion reduces exposure, but aggressive redaction can remove evidence needed for an investigation. Prefer pseudonymous identifiers and allowlists of logged fields.

Monitor decisions and anomalies

Record the authenticated principal or pseudonymous identifier, tenant, operation, object type, policy or control that ran, decision, reason code, credential identifier, latency, and correlation ID. Do not log raw tokens, API keys, webhook signatures, passwords, or full sensitive payloads.

Alert on sustained authorization denials, credential failures across many accounts, access to many missing object IDs, repeated webhook deliveries, unusual exports, and growth in expensive operations. Baseline by route and customer tier so legitimate volume does not mask attacks.

Every alert needs an owner and response path. Runbooks should cover identifying affected objects, rotating credentials, preserving evidence, and limiting one operation without taking down unrelated traffic. Test that correlation IDs connect gateway, application, job, and downstream records before an incident.

Test continuously

Turn each security rule into automated positive and negative tests. Include missing, malformed, expired, revoked, wrong-issuer, and wrong-audience credentials; cross-tenant and child-object IDs; forbidden functions; unknown fields; boundary and oversized values; ambiguous encodings; replayed webhook deliveries; concurrent requests; and limit behavior just below and above thresholds.

Test failures as carefully as successes. Simulate an unavailable identity provider, stale key cache, authorization policy timeout, rate-limit store outage, webhook deduplication failure, and slow downstream service. Decide in advance which paths fail closed, which can use a bounded cache, and which may degrade safely. Failing closed protects sensitive operations but can amplify a dependency outage; failing open preserves availability but may grant access or consume resources.

Use contract and property-based tests to explore inputs, static analysis for implementation weaknesses, and targeted dynamic tests in a controlled environment. Scanners cannot infer who may approve a refund or export tenant records. Compare deployed routes with inventory, verify retired endpoints are unreachable, and exercise detections with safe synthetic events. Production dry runs must not capture sensitive payloads or permanently replace enforcement.

Frequently asked questions

What is the most important API security best practice?

There is no single control that secures an API. The strongest baseline combines authentication, object-level authorization, strict validation, abuse controls, and monitoring. Authorization on every request is especially important because a valid identity must not imply access to every object.

Is an API gateway enough to secure an API?

No. A gateway is useful for coarse controls, but it usually cannot see application identity, business permissions, or the meaning of a requested operation. Security checks should also run inside the application where that context is available.

How often should API security be tested?

Test security controls in CI for every material change, continuously monitor production behavior, and repeat threat modeling whenever an API adds new data, identities, integrations, or privileged operations.

Application security in your code

Protect your application with Arcjet

Arcjet runs inside your application, where it can use request and identity context to enforce rate limits, detect bots, and block common attacks.