Get and refresh an access token
Exchange your client ID and client secret for an access token, refresh it before expiry, and retry once on 401.
Every Mantic API request needs a short-lived OAuth 2.0 access token. Ask your Mantic contact or support@mantic.com for a client ID and client secret.
Request an access token
Send your client ID and client secret as HTTP Basic credentials to the token endpoint, and save the token in ACCESS_TOKEN. The command reads them from MANTIC_CLIENT_ID and MANTIC_CLIENT_SECRET, exported as in Read your first forecast. This command needs jq:
export ACCESS_TOKEN=$(curl -fsS -X POST "https://app.mantic.com/auth/oauth2/token" \
-u "$MANTIC_CLIENT_ID:$MANTIC_CLIENT_SECRET" \
-d "grant_type=client_credentials" \
-d "resource=https://api.mantic.com" | jq -r .access_token)resource must be exactly https://api.mantic.com, with no trailing slash, or the API rejects the token.
The token endpoint returns the token and its lifetime in seconds:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600
}Send the token in the Authorization header of every API request:
curl "https://api.mantic.com/questions" \
-H "Authorization: Bearer $ACCESS_TOKEN"Every cURL example on this site uses this $ACCESS_TOKEN. To try a request in the API reference, paste the token there.
Refresh by requesting a new token
There is no refresh token: request a new token shortly before expires_in runs out. If a request returns 401, get a new token and retry the request once.
Keep the lifecycle in one helper
mantic_client.py wraps this in ManticClient, which refreshes the token 30 seconds before expiry and retries a request once after a 401. The Python examples on other pages import it:
import os
import time
from typing import Any
import httpx
API_URL = "https://api.mantic.com"
AUTH_URL = "https://app.mantic.com/auth/oauth2/token"
class ManticClient:
def __init__(self) -> None:
self._client_id = os.environ["MANTIC_CLIENT_ID"]
self._client_secret = os.environ["MANTIC_CLIENT_SECRET"]
self._access_token: str | None = None
self._refresh_at = 0.0
self._http = httpx.Client(base_url=API_URL, timeout=30.0)
def _refresh_access_token(self) -> str:
response = self._http.post(
AUTH_URL,
auth=(self._client_id, self._client_secret),
data={
"grant_type": "client_credentials",
"resource": API_URL,
},
)
response.raise_for_status()
payload = response.json()
self._access_token = payload["access_token"]
lifetime = float(payload["expires_in"])
self._refresh_at = time.monotonic() + max(lifetime - 30, 0)
return self._access_token
def _token(self) -> str:
if self._access_token is None or time.monotonic() >= self._refresh_at:
return self._refresh_access_token()
return self._access_token
def request(self, method: str, path: str, **kwargs: Any) -> httpx.Response: # noqa: ANN401
headers = dict(kwargs.pop("headers", {}))
headers["Authorization"] = f"Bearer {self._token()}"
response = self._http.request(method, path, headers=headers, **kwargs)
if response.status_code == 401:
headers["Authorization"] = f"Bearer {self._refresh_access_token()}"
response = self._http.request(method, path, headers=headers, **kwargs)
response.raise_for_status()
return response
def close(self) -> None:
self._http.close()Keep the client secret out of browser code and version control.