Skip to main content

Open APIs for our Customers

Used to set up your API

Introduction

Ignite needs your data. If you move data by hand, it is slow and mistakes are easy. Our REST APIs let you automate this: send data to a data source, or export data from your tables. Both APIs need an API token.

API documentation

Sending data to Ignite

  1. Choose a format: JSON, ND-JSON, Excel (.xlsx) or any standard CSV format. You can gzip the file before you send it.

  2. Create a data source under Data, in Data sources. The example below uses its ID as data_source_id.

  3. If you send JSON, send flat records, not nested objects. This article shows how to flatten JSON in Python.

  4. Write a script or function that sends your files or JSON records to Ignite. Many customers run it as a cloud function, for example in Google Cloud Functions or Azure Functions.

Example in Python. Define data first, as a pandas DataFrame or a list of flat JSON records:

import json
import requests
import pandas as pd

data_source_id = "f3c47953-07bc-45d2-a91e-8f4dbbf7d2b1"
token = "Bearer <your API token>"
IMPORT_API_URL = "https://data.igniteprocurement.com"

### Method to send a dataframe or data to Ignite
def upload_data_ignite(
data,
data_source_id,
token,
import_name,
delete_query_id
):

if isinstance(data, pd.DataFrame):
json_data = json.loads(
data.to_json(orient="records", force_ascii=False)
)
else:
json_data = data


headers = {
"Authorization" : token,
"Content-Type": "application/json"
}

body = {
"dataRepositoryId": data_source_id, # data repo = data source
"data": json_data,
"importName": import_name
}

if delete_query_id:
body["deleteQueryId"]= delete_query_id

payload = json.dumps(body)

response = requests.post(
url=f"{IMPORT_API_URL}/import/",
headers=headers,
data=payload
)

print(response.text)

return response


### invoking the method using the parameters defined at the top
response = upload_data_ignite(
data,
data_source_id,
token,
import_name = "demo_import_42_2023-12-24.json",
delete_query_id=""
)

### We advise that you properly understand how to use this code before using or copying

Exporting data from Ignite

Note: We removed the old endpoints /export/?dataTableId=… and /datatables/ on 1 October 2026. To move an integration to the endpoints below:

Refer to a table by its id, not its UUID: spend, suppliers or contracts. Your own data tables are datatable__<table id>, with underscores instead of the hyphens in the UUID. Your own classification structures are classification_custom__<structure id>.

Rows use column names as keys. To use labels instead, set keyBy to label. Duplicate labels get a suffix. On spend and suppliers, id holds the old rowId value.

Spend rows now carry only supplier_id, supplier_name and supplier_headquarters_country. Read other supplier details from suppliers, where id matches supplier_id on spend. The registration number is in the vat column.

Spend rows include the classification as sc_level_N_id and sc_level_N_name for spend categories, and bu_level_N_id and bu_level_N_name for business units. You do not need to join the classification tables.

Spend-based emissions are in the emissions table, where spend_id matches id on spend. The API no longer has the old lastModified and importId fields. It also no longer has group structure, emissions and aggregation columns on your own data tables.

For the full list of changes, see the migration notes at the top of the API documentation.

The export API is read-only and works per table. It has the tables spend, suppliers, contracts, classification_business_units, classification_spend_categories and emissions. It also has a table for each of your own data tables and classification structures.

Send your API token in the header Authorization: Bearer <token>. Old JWT tokens no longer work.

You can read rows page by page, or export a full table to files. To keep a copy of your data outside Ignite, run a full export on a schedule. Do not use updated_at to find the rows that changed. On suppliers and contracts it does not change when Ignite refreshes enrichment data or someone edits a custom field.

Find your tables and columns

Send a GET request to https://datatable.igniteprocurement.com/tables/. The response lists each table's id and columns. Each column has a name, a label, a dataType and the filter operators it supports (supportedOperators).

Use name in your requests. The label changes when someone renames the column, but the name does not. Columns you have created yourself have names like custom__<field id>.

Read rows

  1. Send a POST request to https://datatable.igniteprocurement.com/tables/{table id}/rows, for example /tables/spend/rows.

  2. Put your options in a JSON request body. All of them are optional:

    • fields: the columns to return. By default you get all columns except array columns, such as supplier tags. To get an array column, list it in fields.

    • filters: conditions by column name, for example {"amount": {"gte": 100}, "transaction_date": {"between": ["2026-01-01", "2026-01-31"]}}. A row must match all of them. For each column, use an operator from its supportedOperators.

    • sort: the order of the rows, for example [{"field": "amount", "direction": "desc"}].

    • pageSize: the maximum number of rows on a page, from 1 to 100,000. The default is 1,000.

    • currency (spend only): the currency of amount, for example SEK. The default is your display currency, or EUR if you have not set one. Ignite converts each amount at the latest daily rate on or before its transaction date. If there is no rate, amount is null.

    • keyBy: set it to label to key rows by column label instead of name. fields, filters and sort still use names.

  3. The rows are in data. If the response contains nextPageToken, send the same body again with pageToken set to that value. Stop when the response has no nextPageToken.

Note: Page tokens expire 24 hours after the first page. A request with an expired token fails with the error code page_token_expired and HTTP status 400 or 410. Check the code, not the status, and start again from the first page.

Example in Python:

import time
import requests

BASE_URL = "https://datatable.igniteprocurement.com"
headers = {"Authorization": "Bearer <your API token>"}

### Send a request, and retry server errors (5xx) after a short delay
def send(method, path, body=None):
for attempt in range(5):
response = requests.request(method, f"{BASE_URL}{path}", headers=headers, json=body)
if response.status_code < 500:
return response
time.sleep(2 ** attempt)
response.raise_for_status()

### 1. List the tables and columns you can export
response = send("GET", "/tables/")
response.raise_for_status()
for table in response.json():
print(table["id"], [column["name"] for column in table["columns"]])

### 2. Read spend rows, one page at a time
body = {
"fields": ["id", "transaction_date", "amount", "supplier_name"],
"filters": {"transaction_date": {"gte": "2026-01-01"}},
"sort": [{"field": "transaction_date", "direction": "desc"}],
"pageSize": 1000
}

rows = []
while True:
response = send("POST", "/tables/spend/rows", body)
if response.status_code in (400, 410) and response.json().get("code") == "page_token_expired":
### The page token expired: start again from the first page
rows = []
body.pop("pageToken", None)
continue
response.raise_for_status()
page = response.json()
rows.extend(page["data"])
if "nextPageToken" not in page:
break
body["pageToken"] = page["nextPageToken"]

print(len(rows))

Export a full table

  1. Send a POST request to https://datatable.igniteprocurement.com/tables/{table id}/exports. You can use fields, filters and currency as for rows. To include array columns, list them in fields. Set format to json (the default) or parquet. The response contains the export id.

  2. Send GET requests to https://datatable.igniteprocurement.com/table-exports/{export id} until status is succeeded or failed. If the export failed, error gives the reason.

  3. When the export has succeeded, download every file in files from its downloadUrl. A large export can have more than one file.

Note: Download links expire after 1 hour. Poll the export again to get new ones. You can poll an export for 24 hours after you start it. After that, start a new export.

Example in Python:

import time
import requests

BASE_URL = "https://datatable.igniteprocurement.com"
headers = {"Authorization": "Bearer <your API token>"}

### Send a request, and retry server errors (5xx) after a short delay
def send(method, path, body=None):
for attempt in range(5):
response = requests.request(method, f"{BASE_URL}{path}", headers=headers, json=body)
if response.status_code < 500:
return response
time.sleep(2 ** attempt)
response.raise_for_status()

### 1. Start the export
response = send("POST", "/tables/spend/exports", {"format": "json"})
response.raise_for_status()
export_id = response.json()["id"]

### 2. Poll until it has finished, for at most 1 hour
deadline = time.time() + 60 * 60
while True:
response = send("GET", f"/table-exports/{export_id}")
response.raise_for_status()
status = response.json()
if status["status"] in ("succeeded", "failed"):
break
if time.time() > deadline:
raise SystemExit(f"Export {export_id} did not finish within 1 hour")
time.sleep(10)

if status["status"] == "failed":
raise SystemExit(f"Export failed: {status.get('error')}")

### 3. Download the files (the download links need no token)
for file in status["files"]:
with requests.get(file["downloadUrl"], stream=True) as download:
download.raise_for_status()
with open(f"{export_id}-{file['name']}", "wb") as f:
for chunk in download.iter_content(chunk_size=1024 * 1024):
f.write(chunk)

For help to plan an integration, see:

Did this answer your question?