Key takeaways:
- Bearer tokens let you access protected APIs by sending them in the Authorization header of your cURL requests.
- You can generate and extract tokens cleanly using OAuth 2.0 flows and tools like jq.
- Most cURL errors stem from expired tokens, incorrect scopes, or formatting issues, all of which are easy to diagnose with verbose output.
If you're sitting in front of a cURL command that you know should work - but it keeps hitting you back with a 401 or 403, then you're in the exact spot where cURL Bearer token starts to matter. This article breaks it all down using real-world cURL examples. You'll get a plain-as-day understanding of what a bearer token is, and what's going on behind the scenes when you see it in the wild.
What is a bearer token?
You know that moment at a fancy hotel when the reception hands you a keycard, and you can just tap your way into your room? A bearer token works the same way. The only difference is what it unlocks. Instead of a door, it unlocks access to an API when you send it in the Authorization header of a cURL request. If you have the token, your API requests go through. If you don't, you get blocked.
In simple terms, a bearer token is a cryptic, encrypted string used to grant access to protected resources in API authentication. It’s called a bearer token because the system does not care about your password at that moment. It cares about the token. Whoever’s holding it gets access, because it functions as the authorization credential for that request.
In a cURL command, you’ll usually see it show up as Authorization: Bearer <token>, which is just a request header attached to your requests. But if it’s an API access tool, why not just use an API key or your usual username and password? The short answer is control.
Bearer token authentication is often used because it gives you controlled, limited access that can be revoked cleanly without turning your whole login setup upside down. You’ll see this same logic behind many cURL requests that use bearer tokens, especially when systems want access to be temporary, traceable, and easy to shut off.
Here’s what that “control” looks like in practice:
- Short-lived access (token expires automatically)
- Revoke one credential without changing the account password
- Issue multiple tokens for different devices or scripts, and kill one if needed
- Delegate access without sharing a password (common in OAuth flows)
To understand when to use and when not to use each of these tools, read the comparison table below:
Your app acts “as that user” after login (common with OAuth)
Not the usual choice for user logins
Can work, but it’s mostly an older approach
You can hand out limited access, like read-only
Often one key = broad access (depends on the API)
Whatever the account can do, this can do too
User approves access without sharing a password
Your server connects to a service using one key
Shows up in older systems that still use username and password
Sometimes used when the service wants short-lived access
Very common for scheduled jobs and backend scripts
Common on legacy APIs
You can kill one token without changing the main password
You rotate or delete the key and update your scripts
You change the password or disable the account
Basic cURL bearer token syntax
Now that you know what a bearer token is, the next step is using it in a real cURL command.
Authorization header format
The syntax is simple. This is the pattern you’ll use most in a cURL command:
curl https://api.example.com/data \
-H "Authorization: Bearer YOUR_TOKEN_HERE"
What each piece means:
- cURL = the tool you’re using
- https://api.example.com/data = the URL endpoint you’re requesting
- -H = “add a header”
- Authorization: Bearer YOUR_TOKEN_HERE = the Authorization header that carries the bearer token
Be careful with the exact formatting inside that Authorization header. It needs Authorization, then a colon and a space, then Bearer, then another space, and finally the token value. If the spacing is off, or the token gets extra characters from copying and pasting, the server may treat it like you sent no token at all - even though your cURL request looks correct.
On top of that, most APIs don’t hand you that token upfront. You get it after you authenticate, often by exchanging credentials at a token endpoint in an OAuth 2.0 flow.
Using environment variables
Because the Authorization header format is so strict, you can easily end up spending ages wondering whether you actually copied the bearer token correctly. A much simpler way to avoid this hassle is to store the token in an environment variable and plug that into your cURL request instead.
That way, your cURL command stays nice and readable, and you're less likely to introduce some stray space or a rogue quote that completely breaks bearer token authentication.
macOS / Linux
Step 1: In Terminal, set the token:
export TOKEN="YOUR_TOKEN_HERE"
Step 2: Use it in your cURL request:
curl https://api.example.com/data \
-H "Authorization: Bearer $TOKEN"
Windows PowerShell
Step 1: Set the token:
$env:TOKEN = "YOUR_TOKEN_HERE"
Step 2: Use it in your cURL request:
curl https://api.example.com/data -H "Authorization: Bearer $env:TOKEN"
Windows CMD
Step 1: Set the token:
set TOKEN=YOUR_TOKEN_HERE
Step 2: Run the request:
curl https://api.example.com/data -H "Authorization: Bearer %TOKEN%"
How to get a bearer token with cURL
We've been assuming up until now that you already have your bearer token in hand, and need to figure out how to send it with a cURL command.
But the thing is, you might not even have that token yet. You might just need to get one before you can start making API calls. This section explains how to go about getting that bearer token in the first place.
OAuth 2.0 client credentials example
Let's assume you've got a server set up with a Bash script that's running all the time. It might be used to pull data every hour, push out updates, or sync your records.
The thing is, the API this script needs to use is protected by authentication. That means a standard cURL request won’t get very far before it hits a brick wall. To get past that wall, your script needs to prove it's got the right to be there, which is where OAuth 2.0 client credentials come in.
Here's how it works: Your API provider gives you two key bits of info to get started:
- client_id: think of this as your app's ID card
- client_secret: this is like the secret handshake your app needs to prove it's the real deal
With those two values in hand, your script can request that token and use it to authenticate with the API for the calls it really needs to make.
It sends a cURL POST request to the provider’s token endpoint that might look like this:
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"
This is just a simple cURL command that posts to that token URL. The Content-Type line is a request header that tells the server what kind of data it should expect.
The grant_type line tells the server you're after a token for an app or script, rather than a user session. Then, you toss in the client ID and secret so the provider can verify that it's really your app requesting the token before they hand one over.
If everything checks out, you will receive a 200 status code indicating success, and the server replies with JSON that includes an access token. That access token is your bearer token. Your script then runs its normal API calls using that Authorization header until the token expires.
Extracting the token automatically
Doing this by hand works, but it gets old very fast. You fire off a cURL POST request with the client ID and secret, then wait for the JSON response that contains the access token.
At that point, you're staring at the response, trying to find access_token, and fingers crossed, you manage to paste it into the Authorization header without accidentally including a stray quote or space that breaks bearer authentication.
A cleaner approach is to use jq. It's a super lightweight command-line tool focused on JSON, and it's literally made for situations just like this one. Instead of manually trying to dig the token out of the response, you tell jq what field you're after, then stick that value into your next cURL request as the authorization bearer token.
Here is how you would do that:
curl -s -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" \
| jq -r '.access_token'
You only add the | jq -r '.access_token' at the end, and voila, no more JSON, just the clean access token for your API.
Troubleshooting common errors
So now you've got everything you need to use your bearer token as an access key, but that doesn't mean every cURL request will be a breeze. Even when you are doing everything right, you can still hit a wall for reasons that are easy to overlook.
Here are the errors you're most likely to run into, what they usually mean, and how to spot the issue quickly.
401 Unauthorized - API authentication failure
A 401 almost always means the API didn't accept your Authorization header. The token might be missing, expired, malformed, or simply not valid for that particular API.
Possible causes include:
- You forgot to send Authorization. Bearer ..., or the header didn't even make it into the request at all.
- The token has expired.
- You accidentally pasted the token with an extra character. Or you picked up a hidden whitespace while copying it.
- You used the wrong token type. People often paste refresh tokens, API keys, or ID tokens when the API expects an access token instead.
- You targeted the wrong environment. A staging token can get rejected by production even if your cURL command looks fine to you.
How to figure out what went wrong:
- Double-check that the header is in your cURL request. Using the -v flag will show you the outgoing headers in your HTTP requests.
- If you're using an environment variable, confirm that it expands correctly:
- On a macOS or Linux, echo "$TOKEN" should show you what you got
- In PowerShell, echo $env:TOKEN should give you the value
- Run the token request again. If a new token works, the old one has probably expired or been revoked.
- Take a look at the WWW-Authenticate response header. Some APIs include a little hint there about what went wrong.
403 Forbidden
A 403 is basically the API telling you, "we know who you are, but you're not allowed to do that." It's not really a formatting problem with the Authorization header. This is usually a permissions issue, or something in the security rules is blocking your cURL request from going through.
Possible causes are:
- Your token might have the wrong scope or role. So it lets you read, but won't let you write, delete, or use admin endpoints.
- The endpoint is locked down to a specific account, tenant, or project. Your authorization token is valid, but it just isn't good for that particular thing you're trying to access.
- There might be IP restrictions. Some APIs are super picky and will only accept HTTP requests from a very specific list of IP addresses.
- There's a WAF (Web Application Firewall) or security layer in the way, blocking you. It can look like a permissions problem, even when your token authentication is spot on.
Fast things to try:
- Try hitting a really simple endpoint that should work for almost any valid token - like a "me" or profile endpoint - and see if that works. Then compare the results.
- Double-check the API documentation to see if you need a certain scope or permission to hit that endpoint, and make sure your token was issued with the right permissions for it.
- Take a look at the response body. Some APIs will return a short reason like "insufficient_scope" or "not permitted" to help you figure out what's going on.
SSL and certificate issues
With SSL errors, the problem lies with your cURL request not making it past the initial SSL check. So the server never even gets to look at your Authorization headers and approve any of your API calls - the token isn't even in play yet.
Common causes for this:
- Your system doesn’t trust the certificate chain (and missing CA certificates is a very common one to have).
- Some corporate firewall or security tool is intercepting HTTPS traffic and re-signing certificates.
- You're trying to call the API by IP address, or using a hostname that is totally out of sync with the certificate.
- Your system clock is off, making a perfectly valid certificate fail validation.
Quick things to try:
- Run the cURL request again, this time with verbose output, so you can actually see the SSL details:
curl -v https://api.example.com/data/
- Have a good read of that verbose output. Usually, when things go wrong with SSL, it's because something is going wrong long before the request headers get sent.
- Try the same URL in a browser on the same network. If the browser is warning you about certificate problems, then this isn’t just a cURL-specific issue.
- And if you are calling the API by IP address, try switching to the proper hostname that matches the certificate, and then run the cURL command again.
Security best practices
Always call the API over https:// so the token is encrypted on the wire.
If you see http://, stop and switch to https:// before you test anything.
Keep the token out of URLs, screenshots, logs, and chat messages. Treat it like a password.
Be careful with curl -v. Verbose output can print your Authorization header into your terminal history or logs.
Store the token in an environment variable so you’re not pasting it into every cURL request.
Keep it out of your command history by using an environment variable, and only swap the variable value when the token changes.
Replace tokens regularly, and revoke anything you suspect has leaked. Don’t leave one token running forever.
If your provider supports short-lived tokens, refresh them in your script before they expire instead of hardcoding a long-lived value.
Conclusion
You now know how to request a bearer token, send it in a cURL Authorization header, and troubleshoot the usual 401/403 issues without wasting time. For higher-volume calls or geo-restricted endpoints, routing your requests through a residential proxy can help keep access consistent. Want more practical cURL examples and fixes? Join our Discord channel!
Can I reuse the same bearer token for multiple cURL requests?
Yes, most times you can reuse the same bearer token across many different requests until it expires or is revoked. That's basically the whole point of using an authorization token in bearer token authentication. You get it once and add it to the Authorization header on each command you send to the server.
What's the difference between a bearer token and a JWT?
A bearer token is basically a usage pattern. It’s any token the server will accept when you send it in the Authorization header. OAuth 2.0 commonly uses bearer tokens as its access tokens, but the token itself can be formatted in different ways.
A JWT (JSON Web Token) is a specific token format with a standard structure and often includes claims you can read after decoding it (unless it’s encrypted). Some OAuth access tokens are JWTs, and some are opaque random strings. So a JWT can be your bearer token, but “bearer token” doesn’t automatically mean “JWT.”
Can I send a bearer token in the URL instead of in the Authorization header?
No. Don't put your token in the URL, that's just asking for trouble. It makes it super easy to leak the token through logs, browser history, proxy logs, analytics tooling, or referrer headers. That leaked token can be used to authenticate other requests.
Stick with the Authorization header. That's the standard way to do things. Only use a URL-based token if the API specifically says you have to, and be aware that it's riskier.
How do I know if my bearer token has expired?
The most obvious sign is that your next request starts coming back with a 401 Unauthorized, often with a hint in the response headers like WWW-Authenticate that tells you the token is no good.
If you're generating the token, many OAuth responses also include an expires_in value so you can estimate how long it will last. If your token is a JWT, it often has an exp claim you can read to see when it's going to expire - but don't assume every token is a JWT.
Is it safe to store a bearer token in an environment variable?
Yes, for local testing or short-lived automation, because it keeps the token out of your command history and avoids mangling the Authorization header in each cURL authorization call. However, environment variables can still be exposed in process dumps, debugging output, or shared environments where other users can snoop on your processes.
Treat it like a password: keep it short-lived, avoid printing it, and change it if you're worried it might have been exposed.