Feed

Feed - це механізм синхронізації даних, який надає доступ до переліку основних сутностей системи Prozorro (тендери, контракти, плани, фреймворки тощо).

Feed є основним інструментом для інтеграції з системою Prozorro, дозволяючи отримувати актуальні дані та відстежувати зміни в режимі реального часу.

Доступні Feed ендпоінти

У системі Prozorro доступні наступні feed ендпоінти:

  • /tenders - перелік тендерів

  • /contracts - перелік контрактів

  • /plans - перелік планів закупівель

  • /frameworks - перелік фреймворків

  • /submissions - перелік подань

  • /qualifications - перелік кваліфікацій

  • /agreements - перелік угод

Приклад запиту:

GET /tenders?descending=1&limit=2 HTTP/1.1

Структура відповіді

Кожен feed ендпоінт повертає JSON відповідь наступної структури:

{
    "data": [
        {
            "id": "82caff5150214ad2b43aa583f7fabcae",
            "dateModified": "2026-01-10T12:52:00.068344+02:00"
        },
        {
            "id": "8e57141320ec463abf6d82c1497702fc",
            "dateModified": "2026-01-09T16:40:51.220780+02:00"
        }
    ],
    "next_page": {
        "offset": "1767969651.246.1.4325efbb2409185921e764a90ea1cb93",
        "path": "/api/2.5/tenders?limit=2&descending=1&offset=1767969651.246.1.4325efbb2409185921e764a90ea1cb93",
        "uri": "https://lb-api-sandbox-2.prozorro.gov.ua/api/2.5/tenders?limit=2&descending=1&offset=1767969651.246.1.4325efbb2409185921e764a90ea1cb93"
    },
    "prev_page": {
        "offset": "1768042320.082.1.5142ecd8a327509ce0580c5133be68f6",
        "path": "/api/2.5/tenders?limit=2&offset=1768042320.082.1.5142ecd8a327509ce0580c5133be68f6",
        "uri": "https://lb-api-sandbox-2.prozorro.gov.ua/api/2.5/tenders?limit=2&offset=1768042320.082.1.5142ecd8a327509ce0580c5133be68f6"
    }
}

Поля відповіді:

  • data: масив об’єктів з мінімальною інформацією (за замовчуванням id та dateModified)

  • next_page: інформація про наступну сторінку

  • prev_page: інформація про попередню сторінку

Сортування за часом модифікації

Feed ендпоінти автоматично сортувати об’єкти за часом модифікації, використовуючи внутрішнє поле public_modified. Це поле містить Unix timestamp (в секундах) і автоматично оновлюється при будь-якій зміні об’єкта.

Важливо: Сортування відбувається за полем public_modified, а не за dateModified. Це забезпечує стабільність пагінації та коректну обробку об’єктів з однаковим часом модифікації.

За замовчуванням (без параметра descending) об’єкти сортируються від старіших до новіших (за зростанням public_modified).

Параметри запиту

offset

Курсор для пагінації. Використовується для отримання наступної або попередньої сторінки результатів.

Формат: {timestamp}.{skip_len}.{skip_hash}

  • timestamp: Unix timestamp - час модифікації останнього об’єкта з попередньої сторінки

  • skip_len: кількість об’єктів з однаковим timestamp, які вже були показані

  • skip_hash: MD5 хеш від посортованих ID об’єктів з однаковим timestamp

Приклад: 1696888800.0.2.c9e589b703dacf9b5ed4833357465084

Для отримання наступної сторінки використовуйте значення offset з поля next_page.offset або просто перейдіть за посиланням next_page.uri що містить значення offset.

limit

Кількість об’єктів на сторінці.

  • За замовчуванням: 100

  • Максимум: 1000

  • Мінімум: 1

  • Якщо вказано більше 1000, автоматично обмежується до 1000

Приклад: /tenders?limit=500

descending

Сортування в зворотному порядку (від новіших до старіших).

  • Якщо параметр присутній (будь-яке значення) - сортування від новіших до старіших

  • Якщо параметр відсутній - сортування від старіших до новіших (за замовчуванням)

Приклад: /tenders?descending=1

mode

Фільтр за режимом об’єктів (тестові або реальні).

  • test: тільки тестові об’єкти

  • _all_: всі об’єкти

  • Якщо не вказано: тільки не тестові об’єкти

Приклади:

  • /tenders?mode=test - тільки тестові тендери

  • /tenders?mode=_all_ - всі тендери

  • /tenders - тільки не тестові тендери (за замовчуванням)

opt_fields

Додаткові поля, які потрібно включити в відповідь.

За замовчуванням feed ендпоінти повертають тільки id та dateModified. Параметр opt_fields дозволяє додати додаткові поля до відповіді.

Детальний перелік доступних полів для feed та детальних ендпоінтів дивіться в Опціональні поля (opt_fields).

Дозволені поля залежать від типу ресурсу, але завжди доступні:

  • public_modified - Unix timestamp часу модифікації (в секундах)

Поля розділяються комою: opt_fields=public_modified,dateCreated

Приклади:

  • /tenders?opt_fields=status

  • /tenders?opt_fields=dateCreated,procuringEntity

  • /tenders?opt_fields=public_modified

Важливо про opt_fields=public_modified:

Поле public_modified містить Unix timestamp в секундах і використовується системою для внутрішнього сортування. Воно може бути корисним для:

  • Точного визначення часу модифікації об’єкта

  • Реалізації власної логіки синхронізації

  • Порівняння часу модифікації різних об’єктів

  • Збереження точки синхронізації для подальшого використання

Приклад відповіді з opt_fields=public_modified:

{
    "data": [
        {
            "id": "82caff5150214ad2b43aa583f7fabcae",
            "dateModified": "2023-10-10T01:00:00+03:00",
            "public_modified": 1696888800.123
        }
    ],
    "next_page": {
        "offset": "1696888800.123.0.abc...",
        "uri": "..."
    }
}

Поле public_modified також можна отримати в детальних ендпоінтах через параметр opt_fields.

Приклад:

GET /tenders/82caff5150214ad2b43aa583f7fabcae?opt_fields=public_modified HTTP/1.1

Пагінація

Feed ендпоінти використовують курсорну пагінацію на основі поля public_modified. Це забезпечує стабільність пагінації навіть при одночасних змінах об’єктів у системі.

Як працює пагінація:

  1. Перший запит: Отримайте першу сторінку без параметра offset

  2. Наступні сторінки: Перейдіть за посиланням next_page.uri

  3. Попередні сторінки: Перейдіть за посиланням prev_page.uri

Важливо про напрямок пагінації:

  • next_page: рухає вас у напрямку поточного сортування

  • prev_page: рухає вас у зворотному напрямку від поточного сортування, але змінює напрямок сортування

Наприклад:

  • Якщо ви використовуєте descending=1 (від новіших до старіших), то:
    • next_pagedescending=1) дасть вам старіші об’єкти

    • prev_page (без descending=1) - це “розворот”, дасть новіші об’єкти, і тепер наступні запити треба робити з next_page (без descending) для отримання новіших об’єктів

  • Якщо ви не використовуєте descending (від старіших до новіших), то:
    • next_page (без descending) дасть вам новіші об’єкти

    • prev_pagedescending=1) - це “розворот”, дасть старіші об’єкти, і тепер наступні запити треба робити з next_page (з descending) для отримання старіших об’єктів

Стратегії синхронізації

Forward Sync: Синхронізація з початку (старіші об’єкти) до кінця (новіші об’єкти)

Для синхронізації всіх даних з початку до поточного моменту:

Крок 1: Ініціалізація
  • Виконайте перший запит без параметрів (сортування від старіших до новіших)

  • Отримайте першу сторінку результатів

Крок 2: Ітерація по сторінках
  • Перейдіть за посиланням next_page.uri для отримання наступної сторінки

  • Кожна наступна сторінка містить новіші об’єкти

  • Повторюйте до отримання порожнього списку в data

Крок 3: Визначення завершення
  • Якщо відповідь містить порожній список в data, це означає, що всі існуючі об’єкти були прочитані

Крок 4: Подальші дії
  • Зупиніть синхронізацію, або

  • Продовжуйте читати оновлення в режимі реального часу, очікуючи появи нових об’єктів

  • При читанні оновлень в режимі реального часу рекомендується додати затримку між запитами при отриманні порожніх відповідей

Backward Sync: Синхронізація від кінця (найновіші об’єкти) до початку (старіші об’єкти)

Для отримання найновіших об’єктів першими:

Крок 1: Ініціалізація
  • Виконайте перший запит з descending=1 (сортування від новіших до старіших)

  • Отримайте першу сторінку результатів з найновішими об’єктами

Крок 2: Ітерація по сторінках
  • Перейдіть за посиланням next_page.uri для отримання наступної сторінки

  • Кожна наступна сторінка містить старіші об’єкти

  • Повторюйте до зникнення поля next_page

Крок 3: Визначення завершення
  • Якщо у відповіді відсутнє поле next_page, це означає, що всі об’єкти були прочитані

Крок 4: Завершення
  • Зупиніть синхронізацію

Forward/Backward Sync: Двонаправлена синхронізація

Для синхронізації в обох напрямках (вперед і назад) та моніторингу нових змін:

Крок 1: Ініціалізація
  • Виконайте перший запит з descending=1 (сортування від новіших до старіших)

  • Отримайте першу сторінку результатів

Крок 2: Backward Sync: Рух назад (читання старих даних від новіших до старіших)
  • Перейдіть за посиланням next_page.uri для отримання наступної сторінки

  • Кожна наступна сторінка містить старіші об’єкти

  • Повторюйте до зникнення поля next_page

  • Якщо у відповіді відсутнє поле next_page, це означає, що всі об’єкти були прочитані

  • Зупиніть синхронізацію

Крок 3: Forward Sync: Рух вперед (читання нових даних в режимі реального часу)
  • Перейдіть за посиланням prev_page.uri для “розвороту” (він автоматично знімає descending=1)

  • Перейдіть за посиланням next_page.uri для отримання наступної сторінки

  • Кожна наступна сторінка містить новіші об’єкти

  • Якщо відповідь містить порожній список в data, це означає, що всі існуючі об’єкти були прочитані

  • Продовжуйте читати оновлення в режимі реального часу, очікуючи появи нових об’єктів

  • При читанні оновлень в режимі реального часу рекомендується додати затримку між запитами при отриманні порожніх відповідей

Отримання повної інформації про об’єкт

Feed ендпоінти повертають тільки мінімальну інформацію про об’єкти. Для отримання повної інформації про конкретний об’єкт використовуйте детальний ендпоінт:

GET /tenders/64e93250be76435397e8c992ed4214d1 HTTP/1.1

Приклад робочого процесу:

  1. Отримайте список об’єктів через feed ендпоінт

  2. Витягніть id з кожного об’єкта

  3. Використайте id для отримання повної інформації через детальний ендпоінт

Затримка появи нових об’єктів у фіді (watermark)

Об’єкти у фіді з’являються з невеликою затримкою після їхнього збереження в базі даних.

Це навмисна поведінка, що захищає від race condition: при паралельних записах MongoDB фіксує час операції на її початку, а не в момент коміту. Через це два об’єкти, збережені майже одночасно, можуть з’явитися в БД у неочікуваному порядку. Без затримки crawler міг би пропустити деякі об’єкти назавжди.

Поточне значення затримки: ~1 секунда.

Практичний ефект:

  • Forward feed (без descending): об’єкти з’являються з затримкою ~1 секунда. Якщо ваш crawler досяг кінця і отримав порожню відповідь — нові об’єкти з’являться за ~1 секунду. При типовій паузі між запитами затримка непомітна.

  • Descending без offset (перша сторінка): також застосовується затримка ~1 секунда. Це критично для коректної двонаправленої синхронізації (backward→forward): offset з першої descending-сторінки гарантовано старіший за 1 секунду, тому використання його як стартової точки для подальшого forward sync не призведе до пропуску об’єктів.

  • Descending з offset (пагінація по历史nich даних): затримка не застосовується — читаються вже закомічені старі записи.

Warning

Backward→forward sync: якщо ви реалізуєте двонаправлену синхронізацію (спочатку descending, потім forward), використовуйте offset з першої descending-сторінки як стартову точку для forward sync без жодних коректувань. Затримка на першій descending-сторінці вже гарантує, що жоден об’єкт не буде пропущений при переході на forward.

See also

Гарантія порядку в API фіді — детальний технічний опис race condition, реалізації watermark та альтернативних підходів (для розробників).

Обмеження та рекомендації

Обмеження швидкості запитів

На публічних ендпоінтах діє обмеження швидкості запитів: 140 запитів на секунду (r/s).

Максимальний ліміт сторінки

Максимальна кількість об’єктів на сторінку: 1000.

Рекомендація: Використовуйте значення limit від 100 до 500 для оптимального балансу між кількістю запитів та обсягом даних.

Фільтрація об’єктів

Автоматично застосовуються наступні фільтри:

  • Тільки публічні об’єкти (статус не draft)

  • Фільтр за mode (тестові/реальні об’єкти)

Об’єкти зі статусами draft не відображаються в feed, навіть якщо вони публічні.