Fortnox API: komplett guide för utvecklare 2026
Fortnox API-guide: OAuth2-flow, endpoints, Python-exempel, rate limits och vanliga fel. Skapa faktura, hämta kunder och bygg integrationer.
Fortnox API är i praktiken det enda produktionsmogna REST-API:et för svensk bokföring med ordentlig dokumentation, stort partnerekosystem och ett rate limit som går att leva med. Det är också det API jag bygger mot i 80 % av uppdragen där en kund vill automatisera fakturering eller läsa in verifikat från ett externt system.
Den här guiden täcker det du behöver för att komma igång på en eftermiddag: OAuth2-flödet, hur du skapar en faktura, hanterar rate limits och undviker de vanligaste felen. Allt testat mot faktisk Fortnox-tenant i april 2026.
Snabbstart på 10 minuter
Innan du ens öppnar en editor behöver du tre saker:
- En Fortnox-licens (din egen demo-tenant räcker — 6 mån gratis via kod NYSTARTAD).
- Ett utvecklarkonto på developer.fortnox.se — här registrerar du din integration och får
client_id+client_secret. - En kund (eller test-tenant) som godkänner din app — utan det får du inte ut någon
access_token.
Integrationen är en OAuth2-app. Fortnox är ovanligt strikta med att man registrerar exakta scopes i förväg (t.ex. invoice, customer, bookkeeping). Begär bara det du faktiskt använder — det är svårare att lägga till scopes senare utan att användaren godkänner om.
OAuth2-flow: så funkar det i Fortnox
Fortnox använder en lätt modifierad variant av OAuth2 Authorization Code-flödet. Stegen är:
- Din app skickar användaren till Fortnox authorization-URL med
client_id,scopeochstate. - Användaren loggar in och godkänner scopes.
- Fortnox redirectar tillbaka till din
redirect_urimed encode-parameter. - Din backend byter
codemotaccess_token+refresh_tokenviaPOST https://apps.fortnox.se/oauth-v1/token. access_tokenär giltig i 1 timme.refresh_tokenanvänds för att förnya.
import requests
import base64
CLIENT_ID = "din-client-id"
CLIENT_SECRET = "din-client-secret"
REDIRECT_URI = "https://dinapp.se/callback"
def exchange_code_for_token(code: str) -> dict:
auth = base64.b64encode(
f"{CLIENT_ID}:{CLIENT_SECRET}".encode()
).decode()
r = requests.post(
"https://apps.fortnox.se/oauth-v1/token",
headers={
"Authorization": f"Basic {auth}",
"Content-Type": "application/x-www-form-urlencoded",
},
data={
"grant_type": "authorization_code",
"code": code,
"redirect_uri": REDIRECT_URI,
},
timeout=15,
)
r.raise_for_status()
return r.json()
Svaret ser ut ungefär så här:
{
"access_token": "eyJ0eXAiOiJKV1Qi...",
"refresh_token": "a1b2c3d4...",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "invoice customer bookkeeping"
}
Viktig detalj: spara refresh_token direkt. När jag testade förra veckan hände det att en kund tappade sin refresh_token och vi fick köra om hela auktorisationsflödet med slutanvändaren. Refresh_token går att återanvända tills användaren återkallar tillstånd.
Autentisering: access_token och refresh
Alla API-anrop skickas med en Authorization: Bearer {access_token}-header mot https://api.fortnox.se/3/. När token löpt ut får du 401 — då förnyar du så här:
def refresh_access_token(refresh_token: str) -> dict:
auth = base64.b64encode(
f"{CLIENT_ID}:{CLIENT_SECRET}".encode()
).decode()
r = requests.post(
"https://apps.fortnox.se/oauth-v1/token",
headers={
"Authorization": f"Basic {auth}",
"Content-Type": "application/x-www-form-urlencoded",
},
data={
"grant_type": "refresh_token",
"refresh_token": refresh_token,
},
timeout=15,
)
r.raise_for_status()
return r.json()
Det vanligaste felet jag stött på är att man glömmer uppdatera den sparade refresh_token efter refresh — Fortnox kan rotera den. Skriv alltid över båda i din databas.
Endpoints översikt
Fortnox /3/-API:et är brett. Här är de endpoints du kommer att använda oftast:
| Endpoint | Beskrivning | Metoder |
|---|---|---|
/3/invoices | Kundfakturor | GET, POST, PUT |
/3/customers | Kunder | GET, POST, PUT, DELETE |
/3/suppliers | Leverantörer | GET, POST, PUT |
/3/vouchers | Verifikat | GET, POST |
/3/accounts | Kontoplan | GET, PUT |
/3/articles | Artiklar/produkter | GET, POST, PUT, DELETE |
/3/orders | Orderläggning | GET, POST, PUT |
/3/offers | Offerter | GET, POST |
/3/financialyears | Räkenskapsår | GET |
/3/employees | Anställda | GET, POST, PUT |
Fullständig referens finns på apps.fortnox.se/apidocs. API:et returnerar både JSON och XML — ange Accept: application/json i alla anrop om du kör Python eller JavaScript.
Skapa en faktura (Python + cURL)
Det här är hello-world för Fortnox-integrationer. Du behöver först en kund (CustomerNumber). Här antar vi att kunden redan finns.
Python
import requests
ACCESS_TOKEN = "din-access-token"
def create_invoice(customer_number: str, rows: list) -> dict:
payload = {
"Invoice": {
"CustomerNumber": customer_number,
"InvoiceDate": "2026-04-15",
"DueDate": "2026-05-15",
"InvoiceRows": rows,
}
}
r = requests.post(
"https://api.fortnox.se/3/invoices",
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
},
json=payload,
timeout=15,
)
if r.status_code == 429:
raise RuntimeError("Rate limit — vänta 5 sek och försök igen")
r.raise_for_status()
return r.json()
rows = [
{
"ArticleNumber": "KONSULT",
"Description": "Konsulttimmar april",
"DeliveredQuantity": 10,
"Price": 1200,
"VAT": 25,
}
]
invoice = create_invoice("1", rows)
print(invoice["Invoice"]["DocumentNumber"])
cURL
curl -X POST https://api.fortnox.se/3/invoices \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"Invoice": {
"CustomerNumber": "1",
"InvoiceDate": "2026-04-15",
"DueDate": "2026-05-15",
"InvoiceRows": [
{"ArticleNumber": "KONSULT", "DeliveredQuantity": 10, "Price": 1200, "VAT": 25}
]
}
}'
Fortnox returnerar hela fakturan med DocumentNumber, Total, Balance m.fl. Spara DocumentNumber — det är den primära nyckeln du använder för att uppdatera, skicka eller makulera fakturan senare.
Hämta kunder
def list_customers(page: int = 1, limit: int = 100) -> dict:
r = requests.get(
f"https://api.fortnox.se/3/customers",
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Accept": "application/json",
},
params={"page": page, "limit": limit},
timeout=15,
)
r.raise_for_status()
return r.json()
data = list_customers()
for c in data["Customers"]:
print(c["CustomerNumber"], c["Name"])
Paginering är något underdokumenterad i Fortnox. MetaInformation i svaret innehåller @TotalResources, @TotalPages och @CurrentPage — använd dem för att iterera. Max limit är 500, men jag brukar köra 100 för att hålla svarsstorleken hanterbar.
cURL-variant för enkel koll
curl https://api.fortnox.se/3/customers?limit=5 \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Accept: application/json"
Webhooks
Fortnox Webhooks ligger i beta enligt dokumentationen 2026 och stöder i dagsläget ett begränsat antal händelser — främst invoice.created, invoice.paid och voucher.created. Du registrerar webhooks via /3/webhooks-endpointen.
def register_webhook(url: str, event: str) -> dict:
payload = {
"Webhook": {
"Url": url,
"Event": event,
}
}
r = requests.post(
"https://api.fortnox.se/3/webhooks",
headers={
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json",
},
json=payload,
timeout=15,
)
r.raise_for_status()
return r.json()
Webhooks signeras med en HMAC-header — verifiera alltid signaturen innan du litar på payloaden. Sekreten får du när webhooken registreras.
Nackdel som är värd att nämna: Fortnox webhooks är långsammare och mindre kompletta än t.ex. Stripes. För kritiska flöden (bokföring av inbetalningar) kompletterar jag alltid med en polling-job som körs var 15:e minut som failsafe.
Rate limits och felhantering
Fortnox rate limit enligt dokumentationen 2026 är 25 requests per 5 sekunder per access_token — alltså ungefär 300 req/min. Det låter generöst men är lätt att träffa om du bulk-importerar kunder eller fakturor.
Felkoderna du kommer se:
| Status | Betydelse | Åtgärd |
|---|---|---|
| 401 | Access_token utgången eller ogiltig | Kör refresh_token-flow |
| 403 | Saknar scope | Re-auktorisera med rätt scope |
| 404 | Resurs finns inte | Kolla DocumentNumber/CustomerNumber |
| 429 | Rate limit | Backa av, vänta 5 sek |
| 400 | Valideringsfel | Läs ErrorInformation.message i svaret |
| 500 | Fortnox-fel | Retry med exponential backoff |
Enkel retry-wrapper:
import time
def with_retry(fn, max_retries: int = 5):
for attempt in range(max_retries):
try:
return fn()
except requests.HTTPError as e:
status = e.response.status_code
if status == 429:
time.sleep(5 * (attempt + 1))
elif status >= 500:
time.sleep(2 ** attempt)
else:
raise
raise RuntimeError("Max retries nådda")
Vanligaste 400-felet jag stött på är att CustomerNumber skickas som int istället för string. Fortnox är petiga med typer — alla ID-fält ska vara strängar.
Begränsningar — var ärlig
Fortnox API är bra, men inte perfekt:
- Ingen separat sandbox. Du testar mot en riktig (men gratis demo-) tenant. Jämför med Visma eEkonomi som har dedikerad sandbox.
- Webhooks är fortfarande begränsade. För realtid i produktion kompletterar du med polling.
- Paginering är inkonsekvent mellan olika endpoints — vissa använder
@TotalPages, andra returnerar bara data. - Svarsformat har legacy-XML-arv — fältnamn är PascalCase och nästlade i konstiga objektnivåer (
Invoice.InvoiceRows.InvoiceRow).
För en utvecklarvinkel på hur Fortnox står sig mot andra bokföringsprogram, se vår jämförelse av bokföringsprograms API:er.
FAQ
Är Fortnox API gratis?
API-åtkomsten i sig är gratis — men du behöver en Fortnox-licens (från 209 kr/mån, se Fortnox-prisanalys) för att ha en tenant att integrera mot.
Finns det en sandbox?
Nej. Fortnox har ingen separat sandbox, utan du kör mot en riktig tenant (ofta demo-tenant med 6 mån gratis). Det är en svaghet jämfört med Visma eEkonomi.
Hur länge gäller en access_token?
1 timme (3600 sekunder). Refresh_token är långlivad och används för att förnya tills användaren återkallar tillstånd.
Vilka språk har officiella SDK:er?
Fortnox publicerar inget officiellt SDK. Det finns community-paket för Python (fortnox-python), PHP och Node — kvaliteten varierar. Jag bygger oftast direkt mot REST-API:et eftersom det är enklare att felsöka.
Kan jag automatisera hela bokföringen via API?
I princip ja — verifikat (/3/vouchers), fakturor och kontoplan är alla tillgängliga. Men slutlig deklaration och årsredovisning sker fortfarande i Fortnox-UI. Se vår guide automatisera fakturering för ett komplett exempel.
Vad kostar det att bygga en integration?
Utvecklartid. Fortnox tar ingen API-avgift för standardintegrationer. Om du paketerar din integration i deras Fortnox Appstore får Fortnox provision.
Fortnox API är det säkraste valet om du bygger en svensk bokföringsintegration idag. Dokumentationen är inte perfekt och webhooks är begränsade, men ekosystemet och stabiliteten slår allt annat på den svenska marknaden. Börja med OAuth2-flödet, skapa en faktura, och bygg ut därifrån.
Läs vidare
Bästa bokföringsprogram 2026 — jämförelse av 12 svenska program
Oberoende test av 12 bokföringsprogram 2026. Priser, features, API-stöd och vinnare per företagstyp. Uppdaterad april 2026.
Fortnox vs Bokio 2026 — vilket passar dig bäst?
Fortnox eller Bokio 2026? Pris, AI-automation, API och funktioner jämfört — med klar vinnare för EF, AB, e-handel och bokföringsbyrå.
Fortnox vs Visma (Spiris) 2026 — vilket är bäst för dig?
Detaljerad jämförelse 2026: pris, API, AI och funktioner. Klar vinnare per scenario — plus vad rebranden Visma eEkonomi → Spiris betyder.