apyhub
Cover illustration for What Is a Bearer Token?
ApyHub

What Is a Bearer Token?

A bearer token is a credential where holding it is the proof. Whoever bears the token gets the access. The server does not check who you are beyond confirming the token is valid.

That is the entire model, and it is also the risk. A stolen bearer token works exactly as well for the thief as for you, until it expires or is revoked.

You send it in the Authorization header:

GET /api/orders HTTP/1.1 Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

The word Bearer, a space, then the token. That format comes from RFC 6750.

01How It Works

Four steps, and the shape is the same across almost every API that uses them.

  1. You authenticate once. Username and password, an OAuth flow, or a client credentials exchange.
  2. The server issues a token. Usually short-lived, often with a refresh token alongside it.
  3. You send the token with every request in the Authorization header.
  4. The server validates it and either serves the request or returns 401.

The token replaces re-authenticating on every call. It is not a session, because the server does not have to remember you: a self-contained token carries its own claims and expiry.

02Bearer Token vs API Key

These get used interchangeably and they behave differently.

API keyBearer token
LifetimeLong-lived, often permanentShort-lived, minutes to hours
IdentifiesAn application or accountUsually a user or a session
Issued byA dashboard, onceAn auth flow, repeatedly
RevocationManual, in a dashboardExpiry, plus revocation
Typical headerCustom, e.g. apy-tokenAuthorization: Bearer
Carries claimsNoOften, if it is a JWT

An API key says which application is calling. A bearer token usually says which user is calling, and for how long.

Some catalogs issue one key covering every service rather than one per API, which reduces the number of credentials you have to store and rotate. ApyHub works this way.

Plenty of APIs accept a long-lived API key in the Authorization: Bearer header, which blurs the line. The header format is not what makes something a bearer token; the model does. If possession alone grants access, it is a bearer credential regardless of what you call it.

03Bearer Token vs Basic Auth

Basic authentication is the older scheme. The client joins a username and password with a colon, Base64-encodes the result, and sends it on every request:

· bash
GET /api/orders HTTP/1.1
Authorization: Basic dXNlcjpwYXNz

Decode dXNlcjpwYXNz and you get user:pass. Anyone who can read the header can do the same in one line of code.

Base64 is encoding, and encoding is reversible by design. It makes the credentials safe to put in a header. It does nothing to hide them. RFC 7617, which defines the scheme, states it is not a secure method of authentication unless it runs over an encrypted transport such as TLS.

The bigger difference is what travels. Basic auth sends the real password with every call, so a single leaked request exposes a credential the user may reuse elsewhere. A bearer token is issued for this API, can expire in minutes, and can be revoked without the user changing their password.

Basic auth still has a place for server-to-server calls over HTTPS where simplicity matters. ApyHub accepts Basic credentials in the Authorization header as an alternative to its token.

04Where OAuth 2.0 Fits

RFC 6750, the spec that defines the Bearer format, is part of OAuth 2.0. That is where most bearer tokens come from.

OAuth 2.0 (RFC 6749) is an authorization framework. It defines how a client obtains a token: a user consenting to an app (the authorization code flow), a service authenticating as itself (client credentials), or a client swapping a refresh token for a new access token. It also defines scopes, which limit what a given token is allowed to do.

The division of labor looks like this:

  • OAuth 2.0 decides who gets a token, with what scopes, for how long.
  • Bearer is how the client presents that token on each request.
  • JWT is one possible format for the token itself.

Once the token is issued, the request looks the same as any other bearer call:

· json
GET /resource HTTP/1.1
Host: api.example.com
Authorization: Bearer abc123

You do not need the full OAuth stack to use bearer tokens. Many APIs issue tokens from a simple login endpoint. OAuth becomes worth its weight when third-party apps act on behalf of your users, or when you need consent screens and fine-grained scopes.

05JWTs Are Not the Same Thing

A common confusion worth clearing up.

Bearer describes how a token is used: send it, and holding it is enough.

JWT describes how a token is formatted: a signed, self-contained structure with claims inside.

Most JWTs are used as bearer tokens. Not all bearer tokens are JWTs. An opaque random string works perfectly well as a bearer token, and the server looks it up rather than decoding it.

Opaque tokens are easier to revoke, because the server holds the state. JWTs scale better, because validation needs no lookup. That is the trade.

Anatomy of a JWT

A JWT (RFC 7519) is three Base64url-encoded parts joined by dots:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Split on the dots and decode each part.

Header. The signing algorithm and the token type.

· json
{
  "alg": "HS256",
  "typ": "JWT"
}

Payload. The claims. sub is the subject (usually a user ID), iat is the issue time as a Unix timestamp. Production tokens also carry exp (expiry), and often iss (issuer), aud (audience) and scopes.

· json
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

Signature. An HMAC-SHA256 of the encoded header and payload, computed with a secret only the issuer holds:

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )

The server recomputes the signature and compares. If anyone edits the payload, say to change sub to another user, the signature no longer matches and the token is rejected.

Two things follow from this. The signature prevents tampering, so the claims can be trusted. The payload is encoded and readable by anyone holding the token, so never put secrets or personal data in it. To inspect a token while debugging, the JWT Decoder API returns the decoded header and payload.

06Sending a Bearer Token in Code

The pattern is the same in every language: get a token, then attach it to each request. The examples below keep credentials in environment variables, per the rules further down.

Python with requests

· python
import os
import requests

AUTH_URL = "https://auth.example.com/token"
API_URL = "https://api.example.com/data"

# 1. Authenticate once and get a token
auth_response = requests.post(
    AUTH_URL,
    json={
        "username": os.environ["API_USERNAME"],
        "password": os.environ["API_PASSWORD"],
    },
    timeout=10,
)
auth_response.raise_for_status()
token = auth_response.json()["access_token"]

# 2. Send it in the Authorization header on every request
response = requests.get(
    API_URL,
    headers={"Authorization": f"Bearer {token}"},
    timeout=10,
)

if response.status_code == 401:
    print("Token missing, malformed or expired. Get a new one.")
elif response.status_code == 403:
    print("Token is valid but lacks permission for this resource.")
else:
    response.raise_for_status()
    print(response.json())

Reuse the token until it expires. On a 401, fetch a new one (or use a refresh token) and retry once.

Node.js with axios

· js
const axios = require("axios");

const AUTH_URL = "https://auth.example.com/token";
const API_URL = "https://api.example.com/data";

async function getData() {
  // 1. Authenticate once and get a token
  const auth = await axios.post(AUTH_URL, {
    username: process.env.API_USERNAME,
    password: process.env.API_PASSWORD,
  });
  const token = auth.data.access_token;

  // 2. Send it in the Authorization header on every request
  const response = await axios.get(API_URL, {
    headers: { Authorization: `Bearer ${token}` },
    timeout: 10000,
  });

  console.log(response.data);
}

getData().catch((err) => {
  const status = err.response?.status;
  if (status === 401) console.error("Token missing, malformed or expired.");
  else if (status === 403) console.error("Token valid, permission denied.");
  else console.error(err.message);
});

For anything beyond a single call, create one axios instance with the header set, or add an interceptor that refreshes the token on a 401.

07Getting an ApyHub Token

ApyHub issues credentials from your workspace. By the definitions above, the token is a long-lived API key: you create it once in the dashboard, and it identifies your workspace across every API in the catalog.

  1. From your dashboard, click API Keys in the left panel, then click + API Keys.Show Image
  2. Select Token as the authentication type, give the token a name, and click Create.Show Image
  3. Download the credentials as a file with Download your credentials, or copy the value to your clipboard. Secrets are generated on the fly and are not stored in plain text, so save the value somewhere safe (a secret manager or environment variable) before you close the dialog.Show Image

ApyHub reads the token from the apy-token header. It does not go in Authorization: Bearer, which is a common mistake when moving between APIs.

bash

curl --request GET 'https://api.apyhub.com/data/dictionary/country' \ --header "apy-token: $APY_TOKEN"

The same call in Python:

python

import os import requests response = requests.get( "https://api.apyhub.com/data/dictionary/country", headers={"apy-token": os.environ["APY_TOKEN"]}, timeout=10, ) response.raise_for_status() print(response.json())

The same rules apply to it as to any bearer credential: possession grants access, so it stays out of URLs, repos and browser storage.

08The Six Rules

  1. Always use HTTPS. A bearer token in plain HTTP is a credential broadcast to the network. This is the one non-negotiable.
  2. Never put it in a URL. Query strings end up in server logs, browser history, referrer headers and analytics. Headers do not.
  3. Never commit it. Not to a public repo, not a private one. Environment variables and secret managers exist for this.
  4. Keep the lifetime short. A token valid for an hour limits the damage from a leak. Pair it with a refresh token for continuity.
  5. Never store it in localStorage for a browser app. Any cross-site scripting flaw reads it instantly. An httpOnly cookie is not readable by JavaScript and is the safer default.
  6. Grant the least privilege the job needs. A token issued to read /user should not unlock /admin. Use scopes or separate tokens per job, so a leaked read-only token stays a read-only problem.

Rule five is the most commonly ignored, and it is the difference between an XSS bug being annoying and an XSS bug being an account takeover.

09When a Token Fails

401 Unauthorized means the token is missing, malformed, or expired. Get a new one.

403 Forbidden means the token is valid and does not grant access to this resource. A new token will not help; the permissions are wrong.

Confusing these wastes a lot of debugging time. A 401 is about the credential. A 403 is about what the credential is allowed to do.

10FAQ

What is a bearer token?

A credential where possession is proof of authorisation. You send it in the Authorization header prefixed with Bearer, and the server grants access to whoever presents a valid one without further identity checks.

What is the difference between a bearer token and an API key?

An API key is typically long-lived and identifies an application. A bearer token is typically short-lived and identifies a user or session. Both are sent with requests, but the lifetime and what they represent differ.

Is a JWT a bearer token?

Usually it is used as one, but the terms describe different things. Bearer is how a token is used; JWT is how it is formatted. An opaque random string can also be a bearer token.

Where should I store a bearer token in a browser app?

In an httpOnly cookie, which JavaScript cannot read. Storing tokens in localStorage means any cross-site scripting vulnerability exposes them immediately.

Why does my request return 401 with a valid token?

Common causes: the token expired, the Bearer prefix is missing, there is whitespace or a newline in the value, or you are sending it to a different environment than the one that issued it.

What is the difference between 401 and 403?

A 401 means the credential is missing or invalid, so getting a new token may help. A 403 means the credential is valid but lacks permission for this resource, so a new token will not help.

Should bearer tokens expire?

Yes, and quickly. Short lifetimes limit the window in which a leaked token is useful. Use a refresh token to obtain new ones without asking the user to log in again.

Can I revoke a bearer token?

It depends on the token type. An opaque token is revoked by deleting its record on the server, and the next request fails. A JWT is validated without a lookup, so it stays valid until its exp claim unless the server also checks a denylist of revoked token IDs. OAuth servers often expose a revocation endpoint defined in RFC 7009 that invalidates access and refresh tokens. Resource servers validating JWTs locally may keep accepting a revoked token until it expires, which is why short lifetimes matter. For a long-lived key such as an ApyHub token, create a new one under API Keys, move your application to it, and retire the old one.

What happens if a bearer token is compromised?

Whoever holds it can call the API as you until it expires or is revoked. The server has no way to tell the attacker's requests from yours. Revoke or rotate the token immediately, check logs for requests you did not make, and find how it leaked (a commit, a log line, a URL) before issuing the replacement. Short lifetimes and least-privilege scopes decide how bad the damage is: a 15-minute read-only token is a small incident, a permanent admin token is a large one.

11Related

Sources:

12About ApyHub

ApyHub is a curated API catalog for developers, teams and AI agents: file conversion, data validation, OCR and extraction and more across 20 categories. One key covers all of it, every endpoint is MCP-ready so AI agents can discover and call them directly, and every service page has a playground for testing before you build.

EU-based and EU-hosted, which keeps data residency simple for teams with GDPR obligations.

Browse the catalog | Get a free API key - no credit card required.