Mental model
Every REST API needs to know who you are before it answers. Cisco objective 2.7 says “Utilize common API authentication mechanisms: basic, custom token, and API keys”. These are the three big ones. In practice there is a fourth that shows up constantly: bearer tokens (used by OAuth2).
All four end up as a header on the request. The server reads the header, decides if you are allowed, and either answers your request or returns 401.
Four schemes side by side
| Scheme | Header shape | Where used | Cisco example |
|---|---|---|---|
| HTTP Basic | Authorization: Basic <base64(user:pass)> | Older APIs, internal tools | Legacy IOS-XE RESTCONF (optional) |
| API Key (custom header) | X-API-Key: abc123... or X-Cisco-Meraki-API-Key: ... | Vendor dashboards | Meraki Dashboard, Webex |
| Bearer token | Authorization: Bearer eyJhbGci... | OAuth2 / OIDC flows | Catalyst Center, modern Cisco APIs |
| Custom token | Varies — X-Auth-Token: abc, token=abc query param | Older / vendor-specific | NX-API, legacy ACI |
HTTP Basic auth
curl
curl -u admin:cisco123 https://api.example.com/devices
-u encodes the credentials and sets Authorization: Basic <base64> for you.
Python requests
r = requests.get("https://api.example.com/devices", auth=("admin", "cisco123"))
When to use
Internal admin tools; APIs that have not moved to OAuth yet. Always over HTTPS; Basic auth over plain HTTP sends credentials in near-plaintext (base64 is encoding, not encryption).
API key
Long opaque string. Usually generated in a vendor dashboard (Meraki, Webex, etc.) and sent as a custom header or query parameter.
curl
curl -H "X-Cisco-Meraki-API-Key: abcdef0123456789" \
https://api.meraki.com/api/v1/organizations
Python requests
r = requests.get(
"https://api.meraki.com/api/v1/organizations",
headers={"X-Cisco-Meraki-API-Key": "abcdef0123456789"},
)
When to use
Dashboard APIs where users generate their own key with a click. Simpler than OAuth, no token refresh needed. Treat the key like a password — rotate on compromise.
Bearer token (OAuth2 style)
Short-lived (hours), issued by an auth server after you log in with user+pass or client ID+secret.
Getting a token (typical two-step flow)
# 1. Trade credentials for a token
TOKEN=$(curl -u user:pass -X POST \
https://api.example.com/oauth2/token \
-d 'grant_type=client_credentials' \
| jq -r .access_token)
# 2. Use the token for subsequent calls
curl -H "Authorization: Bearer $TOKEN" \
https://api.example.com/devices
Python requests
# 1
r = requests.post(
"https://api.example.com/oauth2/token",
auth=("user", "pass"),
data={"grant_type": "client_credentials"},
)
token = r.json()["access_token"]
# 2
r = requests.get(
"https://api.example.com/devices",
headers={"Authorization": f"Bearer {token}"},
)
When to use
Modern APIs. Catalyst Center uses this pattern. Tokens expire (usually 1 hour); your script re-authenticates when it gets a 401.
Custom token
Vendor-specific headers that are not standard Basic or Bearer. Common in older gear.
curl -H "X-Auth-Token: your-token" https://nxapi.example.com/ins
Treat it as “like an API key, but the header name is whatever the docs say”.
The 401 → 403 → 404 ladder
| Status | Means | Fix |
|---|---|---|
| 401 Unauthorized | No credentials OR wrong credentials | Add / fix auth header |
| 403 Forbidden | Auth was accepted but your account lacks permission | Ask an admin for the right role or scope |
| 404 Not Found | URL does not exist OR you are authenticated but not allowed to see it (some APIs return 404 instead of 403 for security) | Verify URL; check docs on 404-vs-403 behaviour |
Security basics
- Never commit tokens / API keys / passwords to Git. Use environment variables and a
.envfile in.gitignore. - Rotate on any suspected leak.
- Use the shortest-lived token your workflow allows.
- Use HTTPS. Always.
FAQ
My Bearer token looks like a huge string with dots in it. What is it? Almost certainly a JWT (JSON Web Token): three base64 segments separated by dots. You can inspect it (not change it) at jwt.io. The payload includes the user, expiry, and permissions.
Do I need to send a session cookie? Cookie auth is common in browser-based tools, uncommon in machine-to-machine REST. If an API asks for a cookie, you probably want its API-key / bearer alternative instead.
How do I debug “it works in curl but not Python”? Add verbose=True effect by printing r.request.headers after the call. Nine times out of ten, Python set an extra default header or encoded the body differently than you expected.
OAuth1 vs OAuth2 — is OAuth1 on the exam? No. 200-901 only cares about the resulting auth header style; OAuth1 (an older flow with signatures) is almost extinct in modern APIs.
