How do I query compliance obligations for Skatteetaten via API?
Obligation lookups need no delegation and no key: ask /api/v1/public/obligationsfor an entity type and you get the template set back, and the deadline calendar answers the same way. Anything scoped to a named company needs an API key, and the company-private tier of facts additionally needs that company's delegation. One thing surprises most integrators: there is no Skatteetaten scope on an Apier key. Apier-key scopes name the data source rather than the agency the filing reaches, so obligations come under read:brreg and filing history under read:altinn. Agency-named Maskinporten scopes are a separate layer, and only a direct integration handles them.
What can I check without a delegation?
More than enough to design against. The zero-auth obligations endpoint takes an entity type and returns the template set for it, with each entry carrying its legal reference as a field. The zero-auth deadline endpoint takes an optional year and returns the recurring calendar, with the timezone, the entity types each entry applies to, and any weekend or holiday adjustment already applied. Neither call names a company, so neither needs anyone's permission.
That combination answers the question most projects actually start with. Before you know which customers you will have, you want to know what a Norwegian limited company owes, how a sole proprietorship differs, and what the reporting rhythm looks like across a year. You can build and test the whole shape of your product against those two endpoints and add company-specific behaviour later.
What you cannot get without a key is anything about a specific organisation, and that is a deliberate line rather than a pricing one. Company data touches board members, signing authority and filing behaviour, which is why it sits behind an identified caller even on the free tier. The compliance automation guide covers which facts are derivable from the open record and which are not.
Which Skatteetaten scopes exist on an Apier key, and what do they cover?
None, and the reason is worth understanding because it will save you a support ticket. Scopes on an Apier key name the data source the call reads, not the agency the resulting filing eventually goes to. So there is no read:skatteetaten to request, and asking for one gets you nothing.
Keep that qualifier in mind if you have read Digdir's documentation, because it is the source of most of the confusion here. Maskinporten has its own agency-named scopes, granted to a registered client and presented on the wire to the agency itself. Those are real, and they are a different layer from the scopes on an Apier key. If you integrate directly you deal with both; through a broker the wire scopes belong to the broker and never appear in your configuration.
What you actually need depends on which endpoint you are calling. Obligations, deadlines and company context read registry facts, so they sit under read:brreg. Filing history reads Altinn instances, so it sits under read:altinn. The dry-run validation surface uses read:actions, which is a read prefix on a write-shaped path because a dry run produces no upstream side effects. The audit trail has its own scope, read:audit, so an operator can grant company-data reads without also exposing the forensic log.
The write-side prefixes exist in the vocabulary but are not assignable to any key. That means a scope list is a factual record of which sources an integration touched rather than a statement of how much it was trusted, which is the property you want when someone asks what your system had access to.
How do I tell whether a filing has already been submitted?
Read the filing history for the organisation rather than inferring it from your own records. Your database knows what your product submitted; it does not know what the customer's accountant submitted from somewhere else last Friday. The history endpoint returns what is recorded against the company, paginated with limit and offset, and the response envelope carries the freshness of the read so you can tell how current the answer is.
Treat a filed period as a stop condition before you act, not as a reconciliation step afterwards. An agent that checks history before proposing a filing avoids the single worst failure mode in this domain, which is a duplicate submission that then has to be corrected through a process much slower than the one that created it. Combining the history read with the dry-run validation gives you both halves: whether it has been done, and whether what you are about to send would be accepted.
Where a period genuinely has to be re-sent, the idempotency key is the safety net rather than your own retry logic. A key makes a repeated call replay the stored response instead of executing twice, which turns an ambiguous network timeout from a decision you have to make into a call you can simply repeat. Build that in before you need it, because the moment you need it is the moment you least want to be reasoning about it.
Make the first call
The first request needs nothing and returns what a sole proprietorship must file. The second is the company-scoped history read, which needs a key and the customer's delegation.
# Zero-auth: what a Norwegian sole proprietorship must file.
curl -s "https://www.apier.no/api/v1/public/obligations?entity_type=ENK"// Has this period already been filed? Read the history.
const res = await fetch(
"https://www.apier.no/api/v1/company/999999999/filing-history?limit=20",
{ headers: { Authorization: `Bearer ${process.env.APIER_API_KEY}` } },
);
const { data, _meta } = await res.json();
console.log(data.filings, _meta.data_freshness);Frequently asked questions
- Is there a Skatteetaten scope I should ask for on my Apier key?
- Not on an Apier key, and this surprises people. Apier-key scopes name the data source rather than the agency a filing eventually reaches. Obligations and deadlines are reached with read:brreg because their inputs are registry facts, filing history uses read:altinn because that is where the instances live, and dry-run validation uses read:actions. There is no read:skatteetaten to request. Agency-named Maskinporten scopes are a separate layer that only a direct integration deals with.
- What can I query without any delegation at all?
- The obligation template for an entity type, and the recurring deadline calendar. Both are zero-auth: no key, no delegation, no company named. That covers the question most integrations actually start with, which is what a Norwegian limited company or sole proprietorship has to file and roughly when.
- What does a delegation add?
- Access to the company-private tier of facts, which widens which rules the evaluation can reach. Every company-scoped response carries a data_tier field: tier_1 means open registry facts only, tier_2 means the caller held an active delegation. A tier_1 answer is complete over its narrower inputs rather than truncated, and the response says which rules it could not reach.
- How do I check whether a filing has already been submitted?
- Read the filing history for the organisation. It returns the submissions recorded against that company, paginated, with the data freshness of the read on the response envelope. Because it reads Altinn instances it needs read:altinn, and like everything company-scoped it needs the company's delegation before it can answer.
- Can I submit to Skatteetaten through this API?
- Validation yes, binding submission no. The dry-run surface checks a real payload against the real rule set and reports each check. Live submission is gated behind approvals that have not landed, and one action type is not wired for live submission at all and answers with an explicit refusal rather than a stub success.