curl Authentication: Basic Auth, Bearer Token and OAuth Examples
Authenticating API requests with curl is a daily task for developers and DevOps engineers. Every API uses a slightly different authentication scheme. This guide covers all of them - from simple API keys to full OAuth 2.0 flows - with copy-paste examples you can use immediately.
Why Authentication Matters in curl
curl is the most widely used HTTP client for scripting and API testing. When you hit an authenticated endpoint without proper credentials, you receive a 401 Unauthorized or 403 Forbidden response. Understanding the exact authentication scheme an API expects - and how to express it in curl - saves hours of debugging.
The five authentication patterns you will encounter most often:
- HTTP Basic Authentication - username and password, base64-encoded in a header
- Bearer Token / JWT - a signed token passed in the Authorization header
- API Key - a static secret passed in a header or query parameter
- OAuth 2.0 Client Credentials - machine-to-machine flow: exchange a client ID/secret for an access token
- Mutual TLS (mTLS) - client certificate authentication at the TLS layer
1. HTTP Basic Authentication
Basic auth encodes username:password as Base64 and sends it in the Authorization header. curl has a shorthand flag that handles the encoding automatically:
# Using the -u flag (curl handles base64 encoding automatically)
curl -u username:password https://api.example.com/data
# To avoid the password appearing in shell history, omit it
# curl will prompt you securely:
curl -u username https://api.example.com/data
# Equivalent manual form (same result, explicit header)
curl -H 'Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=' \
https://api.example.com/data
Basic auth sends credentials in every request. Always use it over HTTPS - over plain HTTP, the base64-encoded credentials can be decoded trivially by anyone intercepting the traffic.
Never store passwords directly in shell scripts. Use environment variables:
curl -u "$API_USER:$API_PASS" https://api.example.com- this avoids credentials appearing in process listings and shell history.
2. Bearer Token Authentication (JWT)
Bearer token authentication is the most common pattern for modern REST APIs. You obtain a token (usually a JWT) through a login endpoint or OAuth flow, then pass it in subsequent requests via the Authorization header with the Bearer scheme:
# Pass a JWT or opaque bearer token
curl -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' \
https://api.example.com/users/me
# Store the token in a variable for reuse in scripts
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
curl -H "Authorization: Bearer $TOKEN" \
https://api.example.com/users/me
# Combine with a JSON POST
curl -X POST https://api.example.com/orders \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"productId": 42, "quantity": 2}'
JWTs are base64url-encoded and contain a payload you can inspect. Use our JWT Decoder to read the claims inside any token without verifying its signature.
3. API Key Authentication
APIs use different conventions for API keys. The three most common are:
API key in a custom header
# Most common: X-API-Key header
curl -H 'X-API-Key: sk-live-abc123def456' \
https://api.example.com/data
# Some APIs use different header names:
curl -H 'Api-Key: sk-live-abc123def456' \
https://api.example.com/data
curl -H 'x-api-token: sk-live-abc123def456' \
https://api.example.com/data
API key as a query parameter
curl 'https://api.example.com/data?api_key=sk-live-abc123def456'
# URL-encode if the key contains special characters
curl 'https://api.example.com/data?api_key=sk%2Blive%2Babc123'
API key in the Authorization header
# Some APIs use a custom scheme in the Authorization header
curl -H 'Authorization: ApiKey sk-live-abc123def456' \
https://api.example.com/data
# Others expect just the key (Stripe uses this pattern)
curl -H 'Authorization: Bearer sk_live_abc123def456' \
https://api.stripe.com/v1/charges
Consult the API's documentation to confirm the exact header name and format. API keys should never appear in URLs logged by servers or proxies - prefer header-based over query parameter-based delivery when the API offers both.
Decode the JWT Token from Your API Response
Working with a JWT-authenticated API? Paste your token into our free JWT Decoder to inspect the header, payload, and expiry without installing anything.
Open JWT Decoder4. OAuth 2.0 - Client Credentials Flow
Machine-to-machine API access commonly uses the OAuth 2.0 Client Credentials flow. You exchange a client_id and client_secret for an access token, then use that token as a Bearer token for subsequent requests. This is a two-step process:
Step 1: Get the access token
curl -X POST https://auth.example.com/oauth/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials' \
-d 'client_id=your-client-id' \
-d 'client_secret=your-client-secret' \
-d 'scope=read:data write:data'
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read:data write:data"
}
Step 2: Use the token in API calls
# Extract token with jq and store it
TOKEN=$(curl -s -X POST https://auth.example.com/oauth/token \
-d 'grant_type=client_credentials' \
-d 'client_id=your-client-id' \
-d 'client_secret=your-client-secret' \
| jq -r '.access_token')
# Use the token
curl -H "Authorization: Bearer $TOKEN" \
https://api.example.com/data
5. Digest Authentication
Digest authentication is an older scheme that challenges the client with a nonce and expects a hashed response rather than base64-encoded plain credentials. curl supports it natively:
# --digest tells curl to use Digest auth instead of Basic
curl --digest -u username:password \
https://api.example.com/protected
6. Mutual TLS (Client Certificate Authentication)
Some enterprise APIs require client certificates for authentication - the server verifies both its own certificate and the client's. This is called mTLS (mutual TLS):
# Client cert + private key in separate files
curl --cert client.crt --key client.key \
https://api.example.com/secure
# Combined PEM file
curl --cert client-combined.pem \
https://api.example.com/secure
# With a CA bundle to verify the server's cert
curl --cert client.crt --key client.key \
--cacert ca-bundle.crt \
https://api.example.com/secure
# PFX/PKCS#12 format
curl --cert-type P12 --cert client.p12:passphrase \
https://api.example.com/secure
Storing Credentials Safely in Scripts
Never hardcode credentials in scripts. The right approach for each context:
- Environment variables:
TOKEN="$MY_API_TOKEN"- set in CI/CD secrets, not in code - curl netrc file:
~/.netrcstores per-host credentials and curl reads it with--netrc - Secret managers: Retrieve at runtime -
TOKEN=$(aws secretsmanager get-secret-value ...)
# ~/.netrc example (chmod 600 ~/.netrc)
machine api.example.com
login myusername
password mypassword
# Use it in curl
curl --netrc https://api.example.com/data
Debugging Authentication Failures
When a request fails with 401 or 403, use curl's verbose mode to inspect the exact headers being sent:
# -v shows request and response headers
curl -v -H 'Authorization: Bearer your-token' \
https://api.example.com/data 2>&1 | head -50
# -i includes response headers in the output
curl -i -H 'Authorization: Bearer your-token' \
https://api.example.com/data
In the verbose output, look for the line starting with > Authorization: to confirm the header was sent as expected. Check the server's WWW-Authenticate response header for clues about what scheme is expected.
FAQ
What is the difference between -u and -H 'Authorization: Basic ...'?
They produce identical HTTP requests. -u username:password is shorthand - curl base64-encodes the credentials and builds the Authorization header automatically. The explicit -H form requires you to pre-compute the base64 value yourself. Use -u for simplicity and -H when building headers programmatically.
Why does curl send credentials on redirect? Is that safe?
By default, curl preserves the Authorization header when following redirects with -L. This is potentially dangerous if the redirect leads to a different host - you could leak credentials to a third party. Use --location-trusted only when you explicitly trust the redirect destination, and avoid it for sensitive production credentials.
How do I pass multiple authentication headers?
Use multiple -H flags. For example, some APIs use a Bearer token for identity and an API key for rate-limiting:
curl -H 'Authorization: Bearer eyJ...' \
-H 'X-API-Key: sk-live-abc123' \
https://api.example.com/data
How can I tell if a JWT is expired without calling the API?
A JWT's payload contains an exp field - a Unix timestamp. Decode the payload (it is just base64url-encoded JSON) and compare exp to the current time. Our JWT Decoder does this automatically and shows a human-readable expiry date.
What does '401 Unauthorized' vs '403 Forbidden' mean?
401 Unauthorized means authentication is required or has failed - your credentials were missing, expired, or invalid. 403 Forbidden means you are authenticated but not authorised - your credentials are valid, but you lack permission for the specific resource or action. These are distinct problems requiring different fixes.
Use our free tool here → JWT Decoder to inspect Bearer tokens returned by your API and verify claims, expiry, and algorithm without leaving your browser.
Usman has 10+ years of experience securing enterprise infrastructure, managing high-traffic servers, and building zero-knowledge security tools. Read more about the author.