Skip to content

Code examples

The same end-to-end flow (Make your first call) in three languages. Each example reads credentials from environment variables — set them before running:

Terminal window
export E1_CLIENT_ID="<your_client_id>"
export E1_CLIENT_SECRET="<your_client_secret>"
export E1_COMPANY_ID="<your_company_id>"

Each example caches the bearer token in memory and refreshes it before expiry. Don’t persist tokens to disk — see Security — Tokens.

curl

A two-step recipe: fetch a token, capture it, call the API. Add jq to extract the access token cleanly.

Terminal window
TOKEN=$(curl -s -X POST https://auth.api.estimateone.com/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=$E1_CLIENT_ID" \
-d "client_secret=$E1_CLIENT_SECRET" \
-d "scope=speci-finder/read" | jq -r .access_token)
curl -s \
"https://api.estimateone.com/api/supplier/v1/companies/$E1_COMPANY_ID/speci-finder/results" \
-H "Authorization: Bearer $TOKEN" \
-H "x-e1-client-id: $E1_CLIENT_ID" \
| jq '.data[] | { projectId, projectName, tenders: [.tenders[].name] }'

Node.js (fetch)

Built-in fetch (Node 18+). No external dependencies needed.

const TOKEN_URL = 'https://auth.api.estimateone.com/oauth2/token';
const API_BASE = 'https://api.estimateone.com';
const clientId = process.env.E1_CLIENT_ID;
const clientSecret = process.env.E1_CLIENT_SECRET;
const companyId = process.env.E1_COMPANY_ID;
let cached = { token: null, expiresAt: 0 };
async function getToken() {
// Refresh ~60s before expiry
if (cached.token && Date.now() < cached.expiresAt - 60_000) {
return cached.token;
}
const body = new URLSearchParams({
grant_type: 'client_credentials',
client_id: clientId,
client_secret: clientSecret,
scope: 'speci-finder/read',
});
const res = await fetch(TOKEN_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body,
});
if (!res.ok) throw new Error(`token fetch failed: ${res.status}`);
const json = await res.json();
cached = {
token: json.access_token,
expiresAt: Date.now() + json.expires_in * 1000,
};
return cached.token;
}
async function getSpeciFinderResults() {
const token = await getToken();
const url = new URL(
`/api/supplier/v1/companies/${companyId}/speci-finder/results`,
API_BASE,
);
const res = await fetch(url, {
headers: {
Authorization: `Bearer ${token}`,
'x-e1-client-id': clientId,
},
});
if (res.status === 401) {
cached = { token: null, expiresAt: 0 };
throw new Error('unauthorised — token discarded, retry once');
}
if (!res.ok) throw new Error(`api call failed: ${res.status}`);
return res.json();
}
const { data } = await getSpeciFinderResults();
console.log(`Got ${data.length} matched projects`);

Python (requests)

import os
import time
import requests
TOKEN_URL = "https://auth.api.estimateone.com/oauth2/token"
API_BASE = "https://api.estimateone.com"
client_id = os.environ["E1_CLIENT_ID"]
client_secret = os.environ["E1_CLIENT_SECRET"]
company_id = os.environ["E1_COMPANY_ID"]
_cache = {"token": None, "expires_at": 0.0}
def get_token() -> str:
# Refresh ~60s before expiry
if _cache["token"] and time.time() < _cache["expires_at"] - 60:
return _cache["token"]
res = requests.post(
TOKEN_URL,
data={
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
"scope": "speci-finder/read",
},
timeout=10,
)
res.raise_for_status()
payload = res.json()
_cache["token"] = payload["access_token"]
_cache["expires_at"] = time.time() + payload["expires_in"]
return _cache["token"]
def get_speci_finder_results() -> dict:
token = get_token()
res = requests.get(
f"{API_BASE}/api/supplier/v1/companies/{company_id}/speci-finder/results",
headers={
"Authorization": f"Bearer {token}",
"x-e1-client-id": client_id,
},
timeout=15,
)
if res.status_code == 401:
_cache["token"] = None # force refresh on next call
res.raise_for_status()
return res.json()
if __name__ == "__main__":
payload = get_speci_finder_results()
print(f"Got {len(payload['data'])} matched projects")

Generating an SDK

If your stack benefits from a typed client, generate one from the OpenAPI 3.1 spec using your tool of choice (openapi-generator, openapi-typescript, oapi-codegen, etc.). Regenerate periodically to pick up non-breaking additions — see Versioning.