Docs

Veldora serves Norwegian company data, public tenders, addresses, laws and exchange rates, and Finnish company data, over HTTP and MCP. You pay per request in USDC on Base with x402. Base URL: https://veldora.no.

Payments are in USDC on Base mainnet (eip155:8453). You don't need an account or an API key, and we don't charge for failed calls.

Dataset pages: Norwegian companies · Norwegian public tenders · Norwegian public contract awards · Tender alerts · Norwegian addresses · Norwegian laws and regulations · NOK exchange rates

Quickstart

  1. Create a wallet and put a small amount of USDC on Base in it. You do not need ETH for gas. Use a dedicated wallet, not your main one.
  2. Install the client: npm install @x402/fetch @x402/evm viem
  3. Wrap fetch and call an endpoint.
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";

const signer = privateKeyToAccount(process.env.BUYER_PRIVATE_KEY);
const payFetch = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(signer) }],
});

const res = await payFetch("https://veldora.no/v1/companies/923609016");
const { data, meta } = await res.json();

Free routes need no wallet: GET /v1/health, GET /openapi.json, and listing tools at POST /mcp.

Authentication by payment

There are no accounts and no API keys. The payment is the credential. The flow follows x402 v2.

  1. Send the request without payment. Veldora replies 402. The PAYMENT-REQUIRED header holds base64-encoded JSON with the requirements. The body has the same JSON.
  2. Pick the entry in accepts: scheme exact, network eip155:8453, the USDC contract, the exact amount in atomic units (6 decimals) and the recipient.
  3. Sign an EIP-3009 transferWithAuthorization for that amount. The facilitator submits it on chain and pays the gas.
  4. Repeat the same request with the base64 payload in the PAYMENT-SIGNATURE header.
  5. On 200 you get the data and a PAYMENT-RESPONSE header with the settlement receipt, including the transaction hash.
curl -i https://veldora.no/v1/companies/923609016

HTTP/2 402
content-type: application/json
payment-required: eyJ4NDAyVmVyc2lvbiI6Mi...

{"x402Version":2,"resource":{"url":"https://veldora.no/v1/companies/923609016", ...},
 "accepts":[{"scheme":"exact","network":"eip155:8453","amount":"5000","asset":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","payTo":"0x...", ...}]}

Rules that protect you:

Endpoints

All paid endpoints are GET and return JSON. Successful responses use one envelope:

{
  "data": { ... },
  "page": { "page": 0, "size": 20, "total": 1 },
  "meta": {
    "source": "brreg",
    "fetched_at": "2026-10-12T08:15:00Z",
    "stale": false,
    "license": "NLOD-2.0",
    "attribution": "Contains data under the Norwegian licence for Open Government data (NLOD) distributed by Brønnøysundregistrene."
  }
}

page appears on list endpoints only. We reject unknown query parameters with 400 unknown_parameter. Organisation numbers are 9 digits with a valid mod-11 check digit.

Look up a company $0.005

GET /v1/companies/{orgnr}

One legal entity from Enhetsregisteret, cleaned.

ParameterTypeDescription
orgnr (path)string, 9 digitsOrganisation number, mod-11 checked.
{
  "data": {
    "orgnr": "923609016",
    "name": "EQUINOR ASA",
    "name_masked": false,
    "org_form": { "code": "ASA", "description": "Allmennaksjeselskap" },
    "status": "active",
    "bankrupt": false,
    "bankrupt_date": null,
    "winding_up": false,
    "winding_up_date": null,
    "forced_dissolution": false,
    "deleted_date": null,
    "founded": "1972-09-18",
    "registered_date": "1995-02-09",
    "vat_registered": true,
    "in_business_register": true,
    "industry": [{ "code": "06.100", "description": "Utvinning av råolje" }],
    "employees": 4000,
    "sector_code": "2100",
    "address": {
      "street": "Forusbeen 50",
      "postal_code": "4035",
      "city": "STAVANGER",
      "municipality": "STAVANGER",
      "municipality_number": "1103",
      "country_code": "NO"
    },
    "postal_address": null,
    "website": "www.equinor.com",
    "parent_orgnr": null,
    "last_annual_accounts_year": 2025,
    "links": { "brreg": "https://virksomhet.brreg.no/nb/oppslag/enheter/923609016" }
  },
  "meta": { "source": "brreg", "fetched_at": "2026-10-12T08:15:00Z", "stale": false, "license": "NLOD-2.0", "attribution": "..." }
}

Status is one of active, bankrupt, winding_up, forced_dissolution, deleted. If several apply, the first of deleted, bankrupt, forced dissolution, winding up wins. The values above are an illustration of the shape.

Tenders published by a company $0.01

GET /v1/companies/{orgnr}/tenders

Tenders where this organisation is the buyer, linked by organisation number.

ParameterTypeDescription
orgnr (path)string, 9 digitsBuyer organisation number.
openbooleanOnly tenders whose deadline has not passed. Default false.
pageinteger, 0 or moreDefault 0.
sizeinteger, 1 to 100Default 20.

The response has the same shape as tender search below, with meta.source set to ted or ted+doffin.

Contracts won by a company $0.01

GET /v1/companies/{orgnr}/awards

Public contracts this organisation has won, from Norwegian contract award notices on TED and Doffin in the last 180 days. Newest first.

ParameterTypeDescription
orgnr (path)string, 9 digitsWinner organisation number.
pageinteger, 0 or moreDefault 0.
sizeinteger, 1 to 100Default 20.

Each item is a tender record (same shape as tender search) with an award object:

"award": {
  "winners": [{ "orgnr": "990432368", "name": "TWODAY AS" }],
  "other_winners": 0,
  "value": { "amount": 9402000, "currency": "NOK" },
  "decided": "2026-09-30",
  "concluded": "2026-10-01"
}

Winner names come from Brønnøysundregistrene. Sole proprietorships stay unnamed (name: null). other_winners counts winners without a Norwegian organisation number. Award notices also carry award in tender search and in tenders by buyer; other notices have award: null.

Search public tenders $0.01

GET /v1/tenders

Norwegian public procurement notices from Doffin and TED. Notices published in both appear once, with links to both. Ids are doffin:… or ted:….

ParameterTypeDescription
qstring, 2 to 100 charsWords in title or description.
cpvstringCPV code or prefix. Up to 10, comma-separated. 72 matches all IT services.
regionstringNUTS code (NO, NO08, NO081) or 2-digit county number.
deadline_fromdateDeadline on or after this date, YYYY-MM-DD.
deadline_todateDeadline on or before this date.
buyer_orgnrstring, 9 digitsOnly tenders from this buyer.
openbooleanOnly tenders whose deadline has not passed. Default true.
pageinteger, 0 or moreDefault 0.
sizeinteger, 1 to 100Default 20.
{
  "data": [
    {
      "id": "ted:123456-2026",
      "source": "ted",
      "publication_number": "123456-2026",
      "title": "Rammeavtale for IT-konsulenttjenester",
      "description": "...",
      "buyer": { "name": "Statens vegvesen", "orgnr": "971032081", "country": "NO", "city": "Oslo" },
      "cpv": ["72000000"],
      "main_cpv": "72000000",
      "regions": ["NO081"],
      "notice_type": "cn-standard",
      "procedure_type": "open",
      "contract_nature": "services",
      "published": "2026-10-01",
      "deadline": "2026-11-01T12:00:00+01:00",
      "estimated_value": { "amount": 5000000, "currency": "NOK" },
      "links": { "ted": "https://ted.europa.eu/en/notice/-/detail/123456-2026" }
    }
  ],
  "page": { "page": 0, "size": 20, "total": 1 },
  "meta": { "source": "ted", "fetched_at": "2026-10-12T04:00:00Z", "stale": false, "as_of": "2026-10-12", "license": "TED reuse terms (Commission Decision 2011/833/EU)", "attribution": "..." }
}

The example values are illustrative. buyer.orgnr is null when the notice has no valid Norwegian number. estimated_value is null when the notice gives none. description is cut at 2000 characters.

Get one tender $0.005

GET /v1/tenders/{id}

One notice with buyer, CPV codes, regions, deadline and the link to Doffin or TED.

ParameterTypeDescription
id (path)stringTender id such as ted:123456-2026.

The response is { "data": { ...tender }, "meta": { ... } } with the same tender fields as above.

Search addresses $0.005

GET /v1/addresses

Search Norwegian street and cadastral addresses from Kartverket by text, municipality or postcode. Give at least one of q, municipality or postcode.

ParameterTypeDescription
qstring, 2 to 100 charsAddress text, e.g. Karl Johans gate 1.
municipalitystring, 4 digitsMunicipality number, e.g. 0301 for Oslo.
postcodestring, 4 digitsPostcode, e.g. 0154.
pageinteger, 0 or moreZero-based page. Default 0.
sizeinteger, 1 to 50Results per page. Default 10.
{
  "data": [
    {
      "address": "Karl Johans gate 1", "street": "Karl Johans gate", "number": 1, "letter": null, "extra_name": null,
      "postcode": "0154", "city": "OSLO",
      "municipality": { "number": "0301", "name": "OSLO" },
      "coordinates": { "lat": 59.9112, "lon": 10.7415 },
      "cadastre": { "gnr": 208, "bnr": 12, "fnr": 0, "snr": null },
      "units": 4, "type": "road", "position_verified": false, "updated": "2024-01-01"
    }
  ],
  "page": { "page": 0, "size": 10, "total": 1 },
  "meta": { "source": "kartverket", "fetched_at": "2026-10-13T08:15:00Z", "stale": false, "license": "CC-BY-4.0", "attribution": "..." }
}

letter and extra_name are null when the address has none. units counts the registered units (bruksenheter) at the address. type is road or cadastre. coordinates are WGS 84 (EPSG:4258). city is as Kartverket gives it. updated is the date Kartverket last changed the record.

Addresses near a point $0.005

GET /v1/addresses/nearby

Addresses within a radius of a point, nearest first. Each item also has distance_m. Coordinates must be in Norway (latitude 57 to 72, longitude 4 to 32).

ParameterTypeDescription
latnumber, 57 to 72Latitude in degrees (WGS 84).
lonnumber, 4 to 32Longitude in degrees (WGS 84).
radiusinteger, 1 to 1000Radius in metres. Default 100.
pageinteger, 0 or moreZero-based page. Default 0.
sizeinteger, 1 to 50Results per page. Default 10.

The response has the same shape as address search, with distance_m (metres from the point) on every item.

Search laws and regulations $0.01

GET /v1/laws

Search current Norwegian laws and central regulations from Lovdata. With q, a document matches when every word is in its title or short title, or when every word is in one of its sections. Title matches come first, laws before regulations, then documents with more matching sections. Each result carries up to three matching sections as short snippets, never the full text. Without q the route lists documents by title.

ParameterTypeDescription
qstring, 2 to 100 charsUp to 8 words, case-insensitive, e.g. personvern behandlingsansvarlig.
typelaw or regulationOnly laws (lov) or only central regulations (forskrift).
ministrystring, 2 to 100 charsPart of the ministry name, e.g. Justis.
pageinteger, 0 or moreZero-based page. Default 0.
sizeinteger, 1 to 50Results per page. Default 10.
{
  "data": [
    {
      "id": "lov/2018-06-15-38", "type": "law",
      "title": "Lov om behandling av personopplysninger (personopplysningsloven)",
      "short_title": "Personopplysningsloven – popplyl",
      "date": "2018-06-15", "ministry": "Justis- og beredskapsdepartementet",
      "last_changed": "2026-01-01", "in_force": true,
      "url": "https://lovdata.no/dokument/NL/lov/2018-06-15-38",
      "title_match": false, "section_matches": 50,
      "matches": [{ "section": "§ 4", "heading": "Behandlingsansvarlig", "snippet": "…" }]
    }
  ],
  "page": { "page": 0, "size": 10, "total": 1 },
  "meta": { "source": "lovdata", "fetched_at": "2026-10-13T05:30:00Z", "stale": false, "as_of": "2026-10-13", "license": "NLOD 2.0", "attribution": "..." }
}

id is the Lovdata id: lov/… for laws, forskrift/… for regulations. in_force is false only when a dated entry into force is still ahead. section_matches counts all matching sections; matches shows the first three. Without q the items have no title_match, section_matches or matches.

Get one law or regulation $0.005

GET /v1/laws/{id}

Metadata and table of contents for one law or regulation: section ids, chapters and headings, without the text.

ParameterTypeDescription
id (path)stringLovdata id as written in the path, slash included: /v1/laws/lov/2005-06-17-62. lov-2005-06-17-62 and lov%2F2005-06-17-62 also work.
{
  "data": {
    "id": "lov/2005-06-17-62", "type": "law",
    "title": "Lov om arbeidsmiljø, arbeidstid og stillingsvern mv. (arbeidsmiljøloven)",
    "short_title": "Arbeidsmiljøloven – aml",
    "date": "2005-06-17", "ministry": "Arbeids- og inkluderingsdepartementet",
    "last_changed": "2026-07-01", "in_force": true,
    "url": "https://lovdata.no/dokument/NL/lov/2005-06-17-62",
    "section_count": 197,
    "sections": [{ "id": "§ 1-1", "chapter": "Kapittel 1. Innledende bestemmelser", "heading": "Lovens formål" }]
  },
  "meta": { "source": "lovdata", "license": "NLOD 2.0", "attribution": "..." }
}

An unknown id gives 404 before any payment is asked for.

Get one section $0.005

GET /v1/laws/{id}/sections/{section}

The full text of one section (§ or artikkel). Paragraphs are separated by newlines and list items keep their labels (a., b.).

ParameterTypeDescription
id (path)stringLovdata id, as above.
section (path)string, 1 to 60 charsA section id from the table of contents. § 3-1 (URL-encoded as %C2%A7%203-1) and 3-1 both work. If a document repeats an id, later copies are § 1 (2), § 1 (3) and so on.
GET /v1/laws/lov/2005-06-17-62/sections/3-1

{
  "data": {
    "law": { "id": "lov/2005-06-17-62", "type": "law", "title": "...", "url": "https://lovdata.no/dokument/NL/lov/2005-06-17-62" },
    "section": { "id": "§ 3-1", "chapter": "Kapittel 3. Alminnelige krav til arbeidsmiljøet", "heading": "Systematisk helse-, miljø- og sikkerhetsarbeid", "text": "..." },
    "previous": "§ 2-6", "next": "§ 3-2"
  },
  "meta": { "source": "lovdata", "license": "NLOD 2.0", "attribution": "..." }
}

Text that Lovdata gives outside numbered sections (amending laws, annexes) is returned as sections named after their chapter heading, or tekst. Footnotes, amendment notes and provisions that have not entered into force are left out.

Exchange rates $0.005

GET /v1/exchange-rates

Official Norges Bank exchange rates against the Norwegian krone. Without date you get the latest business day; without currencies you get every currency Norges Bank publishes. Use from and to together for a daily series.

ParameterTypeDescription
currenciesstringComma-separated ISO 4217 codes, e.g. USD,EUR. At most 40. Default: all.
datestring, YYYY-MM-DDOne day. Not together with from and to.
fromstring, YYYY-MM-DDStart of a period. Needs to.
tostring, YYYY-MM-DDEnd of a period, inclusive. At most 366 days after from.
{
  "data": {
    "date": "2026-10-02",
    "base": "NOK",
    "rates": [
      { "currency": "JPY", "unit_multiplier": 100, "nok_per_unit": 6.1198 },
      { "currency": "USD", "unit_multiplier": 1, "nok_per_unit": 9.6494 }
    ]
  },
  "meta": { "source": "norges-bank", "fetched_at": "2026-10-13T08:15:00Z", "stale": false, "requested_date": "2026-10-03", "date": "2026-10-02", "not_found": [], "license": "...", "attribution": "Source: Norges Bank." }
}

nok_per_unit is the rate as Norges Bank quotes it: NOK per unit_multiplier units of the currency (100 for e.g. JPY, SEK and DKK, otherwise 1). Divide by unit_multiplier for NOK per single unit. Norges Bank publishes on business days only. If date is a weekend or holiday you get the prior business day: meta.requested_date is what you asked for and meta.date (and data.date) is the day the rates are from. meta.not_found lists requested currencies without a rate. If no rate is found at all you get 404 and are not charged. The trade-weighted indices TWI and I44 are left out.

With from and to the data is { "base": "NOK", "series": [ { "currency": "USD", "unit_multiplier": 1, "observations": [ { "date": "2026-09-28", "nok_per_unit": 9.5069 } ] } ] }, and meta has from and to instead of the dates above.

Look up a Finnish company $0.005

GET /v1/fi/companies/{businessId}

One company from the Finnish trade register (PRH, YTJ open data), cleaned. Texts are in English where PRH provides them.

ParameterTypeDescription
businessId (path)string, NNNNNNN-NFinnish business ID (Y-tunnus), mod-11 checked, e.g. 0112038-9. Without the dash (01120389) also works.
{
  "data": {
    "business_id": "0112038-9", "eu_id": "FIFPRO.0112038-9", "name": "Nokia Oyj",
    "other_names": [ { "name": "Nokia Networks", "type": "auxiliary" }, { "name": "Nokia Abp", "type": "parallel" } ],
    "company_form": { "code": "OYJ", "description": "Public limited company" },
    "status": "active",
    "industry": { "code": "70100", "description": "Activities of head offices" },
    "address": { "street": "Karakaari 7", "postcode": "02610", "city": "ESPOO", "country": "FI" },
    "website": "www.nokia.com", "registered": "1896-12-19", "ended": null,
    "trade_register_status": { "code": "1", "description": "Registered" },
    "last_modified": "2026-08-19T10:04:06"
  },
  "meta": { "source": "prh", "fetched_at": "2026-10-13T08:15:00Z", "stale": false, "license": "CC-BY-4.0", "attribution": "..." }
}

status is active, pending (business ID not yet in force), restructuring, winding_up (liquidation), bankrupt or ceased. other_names lists current parallel (parallel) and auxiliary (auxiliary, auxiliary_translation) names; former names are left out. industry is the main line of business in TOL 2008, the Finnish version of NACE. company_form.code is PRH's abbreviation (OY, OYJ, KY, OK and so on) and can be used as the company_form filter. address is the street address, or the postal address when there is none. registered is the first registration date, ended the date of dissolution. An unknown business ID gives 404 and is not charged.

Search Finnish companies $0.01

GET /v1/fi/companies

Search Finnish companies by name (current and former names), town or city, company form, industry and postcode. Give at least one filter. Each page holds up to 100 hits.

ParameterTypeDescription
qstring, 2 to 100 charsName, or part of it.
municipalitystring, 2 to 100 charsTown or city name, e.g. Helsinki.
company_formstringPRH company form code, e.g. OY, OYJ, KY, OK, SÄÄ.
industrystring, 2 to 5 digitsTOL 2008 code or prefix, e.g. 62 or 62010.
postcodestring, 5 digitsPostcode, e.g. 00100.
pageinteger, 0 or moreZero-based page. Default 0.

The response has the same envelope as the lookup, with a list of the records above in data and page as { "page": 0, "size": 100, "total": 991 }. size is PRH's page size; a page can hold fewer hits. total is the number of hits for the whole search.

Free routes $0

Tender alerts $0.25 / 30 days

Register a tender search once and get new matching tenders pushed to your webhook after each nightly refresh of Doffin and TED. One payment covers 30 days.

POST https://veldora.no/v1/alerts?webhook=https%3A%2F%2Fexample.com%2Fhook&cpv=72&region=NO08

Settings go in the query string: webhook (https, public host) and at least one of q, cpv, region and buyer_orgnr. You pay with x402 as for any other route. The 201 response holds the alert id and a secret. We show the secret only once, so store it.

Each delivery is a POST with JSON {"alert_id", "sent_at", "tenders": [...]}, up to 50 open tenders published since you created the alert, each sent once. The Veldora-Signature header reads t=<unix time>,v1=<hex>, where v1 is HMAC-SHA256 of t + "." + body with your secret. Check it before you trust a delivery.

Errors

Errors from the data routes share one shape. Branch on error.code, not on the message.

{ "error": { "code": "invalid_orgnr", "message": "...", "retryable": false, "docs": "https://veldora.no/docs#errors" } }

Every error body has exactly these four fields: code, message, retryable and docs. The content type is application/json. Unknown paths under /v1 return a JSON 404 with code not_found.

Payment errors come back as 402 with a fresh PAYMENT-REQUIRED header. The reason is in the body field error, with a readable text in errorMessage. Sign a new payment for these, unless noted.

StatusCodeMeaning
400invalid_orgnr, invalid_id, invalid_parameterA parameter is malformed or out of range. Not charged.
400missing_filterCompany search needs at least one of q, municipality, nace, org_form.
400enk_name_searchName search is not offered for sole proprietorships.
400paging_limitThe page is past the 10 000 hit limit. Narrow the search.
400unknown_parameterThe query has a parameter the route does not accept. Not charged.
404not_foundNo such organisation or tender, or an unknown path under /v1. Not charged.
402invalid_payment_headerThe header is not base64-encoded JSON.
402unsupported_x402_versionUse x402 version 2 and the PAYMENT-SIGNATURE header.
402unsupported_scheme, unsupported_payload, invalid_payment_payload, invalid_signature_formatThe payload is not an exact EIP-3009 USDC authorization in the expected shape.
402wrong_networkPay on Base mainnet, eip155:8453.
402wrong_assetPay in the USDC contract named in the requirements.
402wrong_recipientThe authorization pays a different address.
402underpaymentThe amount is below the price.
402amount_mismatchThe amount must equal the price exactly.
402payment_expiredThe authorization expires too soon or has expired. Sign a new one.
402payment_not_yet_validvalidAfter is in the future.
402facilitator reason, e.g. insufficient_fundsThe facilitator rejected the payment. The reason is passed through unchanged.
402payment_already_usedThis authorization was used for a different request.
402payment_failedThis payment could not be settled earlier. Sign a new one.
402settlement_failedSettlement failed. Data was withheld. Sign a new payment.
409payment_in_progressThe same payment is being processed. Retry the same header shortly.
429rate_limitedToo many requests. Wait for Retry-After seconds.
503data_unavailableTender data is not loaded yet. Not charged.
503upstream_unavailableThe company register does not answer. Not charged.
503facilitator_unavailableThe payment facilitator did not answer. Not charged. Retry later.
503settlement_pendingSettlement has not confirmed yet. Retry the same header.
503payments_unavailablePayments are not available right now. Retry later.
503temporarily_unavailableSomething we depend on failed before settlement. Not charged.
500internal_errorA bug on our side. Not charged.

Retries and idempotency

Rate limits

Each client IP can send 120 requests per minute. Every API response carries the limit state, so a client can slow down before it is blocked.

HeaderMeaning
RateLimit-PolicyThe policy, for example 120;w=60 (120 requests per 60 seconds). IETF draft-ietf-httpapi-ratelimit-headers.
RateLimitThe current state in the same draft format: remaining requests and seconds until reset.
RateLimit-LimitRequests allowed in the window.
RateLimit-RemainingRequests left in the window.
RateLimit-ResetSeconds until the window resets.
Retry-AfterOn 429 only: seconds to wait before the next request.

A request over the limit gets 429 rate_limited and costs nothing.

Data notes

MCP

Endpoint: https://veldora.no/mcp, streamable HTTP. Listing tools is free. Calling one costs the same as the matching REST route.

ToolRoutePrice
get_companyGET /v1/companies/{orgnr}$0.005
search_companiesGET /v1/companies$0.01
get_company_tendersGET /v1/companies/{orgnr}/tenders$0.01
get_company_awardsGET /v1/companies/{orgnr}/awards$0.01
search_tendersGET /v1/tenders$0.01
get_tenderGET /v1/tenders/{id}$0.005
search_addressesGET /v1/addresses$0.005
addresses_nearGET /v1/addresses/nearby$0.005
search_lawsGET /v1/laws$0.01
get_lawGET /v1/laws/{id}$0.005
get_law_sectionGET /v1/laws/{id}/sections/{section}$0.005
get_exchange_ratesGET /v1/exchange-rates$0.005
get_finnish_companyGET /v1/fi/companies/{businessId}$0.005
search_finnish_companiesGET /v1/fi/companies$0.01

Payment follows the x402 MCP transport:

  1. Call a tool without payment. The result has isError: true and the PaymentRequired object in structuredContent.
  2. Sign the payment as for REST.
  3. Call the tool again with the payload in _meta["x402/payment"].
  4. The result carries the settlement receipt in _meta["x402/payment-response"].

The @x402/mcp client does this for you. If the arguments are wrong, the tool returns isError with invalid_argument before asking for payment.

{
  "mcpServers": {
    "veldora": { "url": "https://veldora.no/mcp" }
  }
}

Command line

The veldora command calls the API from a terminal. It reads the price from the 402 first and refuses to pay more than --max-price (default $0.01). Set VELDORA_PRIVATE_KEY to a wallet key holding USDC on Base to pay.

npm install -g https://veldora.no/cli/veldora-cli-1.1.0.tgz
veldora company 923609016 --dry-run    # show the price, pay nothing
VELDORA_PRIVATE_KEY=0x… veldora company 923609016
VELDORA_PRIVATE_KEY=0x… veldora tenders --cpv 72 --open --size 5

Commands: company, companies, company-tenders, company-awards, tenders, tender, addresses, addresses-nearby, laws, law, law-section, rates, fi-company, fi-companies and health. Run veldora --help for all options. Without installing: npx -y --package=https://veldora.no/cli/veldora-cli-1.1.0.tgz veldora health.

Versioning and deprecation

Markdown for agents

Any page on veldora.no can be fetched as Markdown. Send Accept: text/markdown:

curl -H "Accept: text/markdown" https://veldora.no/docs

The Markdown versions are also available at /index.md, /docs.md, /about.md, /contact.md and /personvern.md. They have the same content as the HTML pages.

Attribution and licences

Network

Veldora takes payment in USDC on Base mainnet (eip155:8453), with real money. Prices are in USD, and one USDC is one US dollar. You do not need ETH, because the facilitator pays the gas. The amount in the 402 response is in atomic units with 6 decimals, so $0.005 is 5000.

Back to top