Syncing data
If your system works with many Narya records at once, such as for reports, exports or reconciliation, keep a copy of those records in your own system. After the first sync, each sync fetches only the records that changed since the last one, including deletions.
This is faster for you and uses far fewer requests. An export of 100,000 transactions that looks up the trading of each one makes about 100,000 requests every time it runs. With a synced copy, each run makes a few.
Syncing is available for enterprises, companies, tradings and acquirers.
How to sync
You store two things: your copy of the records, and your position, which is the updated_at and id of the last record you received.
Request the changes after your position.
GET /integration/v1/tradings?sort=updated_at&include_deleted=true&limit=100
&updated_after=2026-10-22T08:14:02.481305Z
&updated_after_id=0192f6c4-8a20-7d31-b4c5-d6e7f8a9b0c1On your first sync you have no position. Leave out updated_after and updated_after_id to start from the beginning.
Update your copy. For each record in data, save it by its id, replacing any earlier version. If deleted_at is set, delete it from your copy instead.
Move your position to the last record in the page. Its updated_at and id are your new updated_after and updated_after_id. If the page is empty, nothing has changed: keep your position.
Repeat until has_more is false. Your copy is now up to date. Save your position, and use it to start your next sync.
The first sync and every later sync use the same request. Run it as often as your system needs: before each report, every few minutes, or once a day.
Example
Request, continuing from your saved position:
GET /integration/v1/tradings?sort=updated_at&include_deleted=true&limit=100
&updated_after=2026-10-22T08:14:02.481305Z
&updated_after_id=0192f6c4-8a20-7d31-b4c5-d6e7f8a9b0c1Response, with most fields left out:
{
"data": [
{
"id": "0192f6c4-9b31-7e42-a5d6-e7f8a9b0c1d2",
"name": "Kedai Ali (Shah Alam)",
"updated_at": "2026-10-22T08:20:11.902117Z",
"deleted_at": null
},
{
"id": "0192f6c4-9c02-7a11-b2c3-d4e5f6a7b8c9",
"name": "Kedai Ali (Klang)",
"updated_at": "2026-10-22T08:31:47.116842Z",
"deleted_at": "2026-10-22T08:31:47.116842Z"
}
],
"page": 1,
"limit": 100,
"total_count": 2,
"total_is_capped": false,
"total_pages": 1,
"has_more": false
}What to do with it:
| Record | Action |
|---|---|
| Kedai Ali (Shah Alam) | Save it, replacing your earlier version. |
| Kedai Ali (Klang) | deleted_at is set: delete it from your copy. |
| Your position | Move it to the last record: updated_after=2026-10-22T08:31:47.116842Z and updated_after_id=0192f6c4-9c02-7a11-b2c3-d4e5f6a7b8c9. |
has_more is false, so your copy is up to date. The response has the same shape as any list. Here, total_count is the number of changes after your position.
Parameters
| Parameter | Description |
|---|---|
sort=updated_at | Returns records in the order they changed, oldest first, ordered by updated_at and then id. |
include_deleted=true | Also returns deleted records, with deleted_at set. Without it, lists never include deleted records. |
updated_after | Returns records changed after this time. Send updated_at exactly as you received it. |
updated_after_id | Sent with updated_after, from the same record. Among records changed at exactly that time, returns only those after this id. |
limit | Records per page, up to 100. |
To start from a known time, for example when you already have a copy from an export, send updated_after on its own.
Good practices
- Keep one position per list, and save it only after you have saved the page.
- Identify records by
id, notexternal_ref. A deleted record'sexternal_refcan be given to a new record. - Use
limit=100to make as few requests as possible. - If you get
429, wait for the seconds inRetry-Afterand send the same request again. See Rate limits.
Related records
Each record carries its parents' ids: a company its enterprise_id, a trading its company_id and enterprise_id. Sync each list your system uses, and join them in your own system by id.
When to read a record directly
Fetch a single record by id when you need its state right now, for example to show it to a user or to check it just before you change it. Use your copy for work across many records.
Common questions
No. When a record changes, its updated_at moves to the time of the change, which is later than your position. The next sync returns it.
| Trading | updated_at | Returned on your next sync? |
|---|---|---|
| A | 00:01 | No: unchanged |
| C | 00:10 | No: this is your position |
| B | 00:20 | Yes: it changed after your position |
Records saved together, for example in one import, can share exactly the same updated_at. updated_after_id makes sure each of them is returned once.
Say C, D and E all changed at 00:10, and your last page ended at C:
| Trading | updated_at | Returned with updated_after=00:10 and updated_after_id=C? |
|---|---|---|
| C | 00:10 | No: you already have it |
| D | 00:10 | Yes |
| E | 00:10 | Yes |
updated_after_id only compares records with the same updated_at as your position. A record with a later updated_at is always returned, whatever its id.
A change gets its updated_at when Narya starts saving it, but a sync can only see it once the save has finished. If your position moved past a change that was still being saved, that change would end up behind your position and never be returned.
To prevent this, a sync returns only changes that are at least 30 seconds old, and every save finishes well within that time. No change is skipped, and you do not need to overlap your syncs or re-read records.
Not for the same change. Your position only moves forward, so each change is returned once. A record is returned again only in these cases:
| Situation | What to do |
|---|---|
| The record changed again | It has a later updated_at. Save the new version. |
| Your sync stopped after saving a page but before saving your position | Request that page again. Saving by id overwrites the same records, so your copy stays correct. |
This is why you save records by id, and save your position only after the page.
With include_deleted=true, a deleted record is returned once more, after your position, with deleted_at set to the time it was deleted. Delete it from your copy. Without include_deleted=true, deleted records never appear in a list.
Start again from the beginning by leaving out updated_after and updated_after_id. Every record is returned again, and you replace your copy by id.