> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.fynapse.app/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.fynapse.app/_mcp/server.

# Data Ingestion via API

> Learn how to use the Fynapse REST API to send data for ingestion

## Overview

There are two types of data you can ingest into Fynapse:

* financial data, such as Business Events and Journals
* reference and transaction data

To learn what happens to the data after ingestion, refer to [Finance Data Service](/documentation/data-management/finance-data-service/overview).

## Ingesting Data Using REST API

This section covers the steps required to ingest data into Fynapse using REST API.

### Processing Diagram

The diagram below illustrates the flow of API calls during ingestion:

### Example

This example refers to numbered steps on the diagram above.

#### Opening an Ingestion Session

When you want to ingest data using REST API you have to open an ingestion session (1) and generate an Ingestion ID (1a) using the **Create an Ingestion** endpoint:

### Request

POST [https://api.fynapse.com/api/v1/ingestions](https://api.fynapse.com/api/v1/ingestions)

**`Ingestion Created`**

```curl Ingestion Created
curl -X POST https://api.fynapse.com/api/v1/ingestions \
     -H "Authorization: Bearer <token>"
```

**`Ingestion Created`**

```python Ingestion Created
import requests

url = "https://api.fynapse.com/api/v1/ingestions"

headers = {"Authorization": "<token>."}

response = requests.post(url, headers=headers)

print(response.json())
```

**`Ingestion Created`**

```javascript Ingestion Created
const url = 'https://api.fynapse.com/api/v1/ingestions';
const options = {method: 'POST', headers: {Authorization: '<token>.'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

**`Ingestion Created`**

```go Ingestion Created
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.fynapse.com/api/v1/ingestions"

	req, _ := http.NewRequest("POST", url, nil)

	req.Header.Add("Authorization", "<token>.")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

**`Ingestion Created`**

```ruby Ingestion Created
require 'uri'
require 'net/http'

url = URI("https://api.fynapse.com/api/v1/ingestions")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = '<token>.'

response = http.request(request)
puts response.read_body
```

**`Ingestion Created`**

```java Ingestion Created
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.fynapse.com/api/v1/ingestions")
  .header("Authorization", "<token>.")
  .asString();
```

**`Ingestion Created`**

```php Ingestion Created
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.fynapse.com/api/v1/ingestions', [
  'headers' => [
    'Authorization' => '<token>.',
  ],
]);

echo $response->getBody();
```

**`Ingestion Created`**

```csharp Ingestion Created
using RestSharp;

var client = new RestClient("https://api.fynapse.com/api/v1/ingestions");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "<token>.");
IRestResponse response = client.Execute(request);
```

**`Ingestion Created`**

```swift Ingestion Created
import Foundation

let headers = ["Authorization": "<token>."]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.fynapse.com/api/v1/ingestions")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

This endpoint initializes an ingestion session and generates a unique identifier, ingestionId, based on a specified 'namespace' and 'entity'. The created ingestion will have a 'pending' status. Once the ingestion session is initialized, it will allow concurrent requests to the `/ingestion/{ingestionId}` endpoint. The sessions will automatically expire after a period of inactivity. By default, session timeout is set to 15 minutes. This can be changed in the request body, if required.

During the duration of a session, you can ingest data divided into chunks (2), i.e. files in JSON format up to 1MB each, into one Entity using the **Create Entity data** endpoint. Each ingestion session has an Ingestion ID, created in the previous step, which will be assigned to all records ingested across all chunks in a given session.

If you are not sure how the data chunk should be defined, you can download a sample using the **Generates an example of a valid request to upload data for a given Entity and Namespace** endpoint (1b).

#### Create Entity Data

The **Create Entity data** endpoint creates one or multiple records for a given 'ingestionId' to be ingested to an Entity. After the data chunk is sent to Fynapse, the Data Ingester retrieves the structure of the given Entity from the Metadata (3). Based on the returned structure (4), the data undergoes structural validation (5) to ensure their structure is correct, i.e. all mandatory fields have data, the data is of a correct type, etc. Once successfully validated, the data is sent to Fynapse (6).

The response is synchronous and returns a 201 status once the data is successfully processed. Any validation or conversion errors are returned with the failed data in the response body. If the ingestion session is already closed (7b), then the request will be rejected with the 409 status. If the ingestion session was manually closed during data upload (7a), the data submitted in the request will still be processed, but no new data will be ingested.

Sample chunk with data to be ingested using a Transaction Entity:

### Request

POST [https://api.fynapse.com/api/v1/ingestions/\{ingestionId}](https://api.fynapse.com/api/v1/ingestions/\{ingestionId})

**`Created`**

```curl Created
curl -X POST https://api.fynapse.com/api/v1/ingestions/ingestionId \
     -H "Authorization: Bearer <token>"
```

**`Created`**

```python Created
import requests

url = "https://api.fynapse.com/api/v1/ingestions/ingestionId"

headers = {"Authorization": "<token>."}

response = requests.post(url, headers=headers)

print(response.json())
```

**`Created`**

```javascript Created
const url = 'https://api.fynapse.com/api/v1/ingestions/ingestionId';
const options = {method: 'POST', headers: {Authorization: '<token>.'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

**`Created`**

```go Created
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.fynapse.com/api/v1/ingestions/ingestionId"

	req, _ := http.NewRequest("POST", url, nil)

	req.Header.Add("Authorization", "<token>.")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

**`Created`**

```ruby Created
require 'uri'
require 'net/http'

url = URI("https://api.fynapse.com/api/v1/ingestions/ingestionId")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = '<token>.'

response = http.request(request)
puts response.read_body
```

**`Created`**

```java Created
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.fynapse.com/api/v1/ingestions/ingestionId")
  .header("Authorization", "<token>.")
  .asString();
```

**`Created`**

```php Created
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.fynapse.com/api/v1/ingestions/ingestionId', [
  'headers' => [
    'Authorization' => '<token>.',
  ],
]);

echo $response->getBody();
```

**`Created`**

```csharp Created
using RestSharp;

var client = new RestClient("https://api.fynapse.com/api/v1/ingestions/ingestionId");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "<token>.");
IRestResponse response = client.Execute(request);
```

**`Created`**

```swift Created
import Foundation

let headers = ["Authorization": "<token>."]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.fynapse.com/api/v1/ingestions/ingestionId")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

> **Info**
>
> There is no limit on the number of chunks you can ingest per session.

#### Managing an Ingestion Session

You can manually force an ingestion session to close before the set session timeout (7) by using the **Change ingestion session status** endpoint:

### Request

PATCH [https://api.fynapse.com/api/v1/ingestions/\{ingestionId}](https://api.fynapse.com/api/v1/ingestions/\{ingestionId})

**`Ingestion Completed`**

```curl Ingestion Completed
curl -X PATCH https://api.fynapse.com/api/v1/ingestions/ingestionId \
     -H "Authorization: Bearer <token>"
```

**`Ingestion Completed`**

```python Ingestion Completed
import requests

url = "https://api.fynapse.com/api/v1/ingestions/ingestionId"

headers = {"Authorization": "<token>."}

response = requests.patch(url, headers=headers)

print(response.json())
```

**`Ingestion Completed`**

```javascript Ingestion Completed
const url = 'https://api.fynapse.com/api/v1/ingestions/ingestionId';
const options = {method: 'PATCH', headers: {Authorization: '<token>.'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

**`Ingestion Completed`**

```go Ingestion Completed
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.fynapse.com/api/v1/ingestions/ingestionId"

	req, _ := http.NewRequest("PATCH", url, nil)

	req.Header.Add("Authorization", "<token>.")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

**`Ingestion Completed`**

```ruby Ingestion Completed
require 'uri'
require 'net/http'

url = URI("https://api.fynapse.com/api/v1/ingestions/ingestionId")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Patch.new(url)
request["Authorization"] = '<token>.'

response = http.request(request)
puts response.read_body
```

**`Ingestion Completed`**

```java Ingestion Completed
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.patch("https://api.fynapse.com/api/v1/ingestions/ingestionId")
  .header("Authorization", "<token>.")
  .asString();
```

**`Ingestion Completed`**

```php Ingestion Completed
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('PATCH', 'https://api.fynapse.com/api/v1/ingestions/ingestionId', [
  'headers' => [
    'Authorization' => '<token>.',
  ],
]);

echo $response->getBody();
```

**`Ingestion Completed`**

```csharp Ingestion Completed
using RestSharp;

var client = new RestClient("https://api.fynapse.com/api/v1/ingestions/ingestionId");
var request = new RestRequest(Method.PATCH);
request.AddHeader("Authorization", "<token>.");
IRestResponse response = client.Execute(request);
```

**`Ingestion Completed`**

```swift Ingestion Completed
import Foundation

let headers = ["Authorization": "<token>."]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.fynapse.com/api/v1/ingestions/ingestionId")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PATCH"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

This endpoint allows you to change the status of an ingestion session from 'pending' to 'closed'. When a session is closed manually using this endpoint, any data undergoing processing will be processed. No new requests for processing data will be submitted.

You can also check the status of the session (8) using the **Check the status of an ingestion** endpoint.

#### Statistics

Additionally, you can retrieve different statistics regarding, inter alia, ingestion using statistics endpoints.

For more details, refer to [Statistics via API](/developer-guides/api-integration/statistics-via-api).