Мок-API

Apify API

Что покрыто в моке Apify API v2, откуда взята спецификация, чем лимиты Apify отличаются от остальных сервисов и как работает мок его MCP-сервера.

На этой странице · 11

Apify — платформа веб-скрейперов и автоматизаций; её единицы исполнения называются акторами. У неё два интерфейса, и APIStend мокает оба: REST API v2 и MCP-сервер, через который с платформой работают ИИ-агенты.

Откуда взята спецификация

ЧтоЗначение
Источникdocs.apify.com/api/openapi.json — публикует сам вендор, прямой ссылкой
Зеркалоне нужно: антибота нет, вход не требуется
Лицензия спецификациине объявлена
Версия спецификацииv2-2026-09-02T154542Z
Снимок2026-09-02

Единственный из четырёх сервисов, для которого не пришлось ни обходить защиту, ни искать чужой снимок. Поэтому в каталоге у его методов стоит происхождение spec, а не mirror: это живая спецификация вендора, а не копия с чьего-то зеркала. Дата снимка тоже не выдумана — она взята из info.version самой спецификации, то есть датирует сборку у Apify, а не наше скачивание.

Обновить: ./scripts/vendor-apify-spec.sh, затем pnpm ingest apify.

Что покрыто

231 операция в 131 пути. Источники ответов:

ЯрусМетодовДоля
example` — пример из спецификации4118 %
schema` — генерация по схеме ответа15366 %
generic` — пустой валидный конверт3716 %

Крупнейшие разделы: хранилища (38), акторы (36), задачи акторов и очереди запросов (по 15), запуски (10), вебхуки (9). Пять методов помечены устаревшими — так же, как их помечает сам Apify.

Подстановка вместо боевого адреса

Версия у Apify входит в путь, а не в хост, поэтому подмена сводится к замене одного хоста на один префикс — остальная часть адреса не меняется вовсе:

https://api.apify.com/v2/actors
https://apistend.ru/apify/v2/actors

Историческое написание /v2/acts/… работает наравне с /v2/actors/…: боевой api.apify.com отвечает на оба, и именно acts стоит в примерах документации Apify и в его собственных клиентах. Каталог от этого не раздваивается — путь приводится к каноничному и попадает в тот же метод.

Авторизация

Оба способа из спецификации Apify работают, и оба принимают ключ песочницы:

# заголовком — основной способ
curl 'https://apistend.ru/apify/v2/actors' \
  -H 'Authorization: Bearer stend_sk_…'

# параметром — им пользуется сам Apify, подставляя токен в адрес вебхука
curl 'https://apistend.ru/apify/v2/actors?token=stend_sk_…'

Различие двух отказов

Apify отвечает token-not-provided, когда токена нет вовсе, и invalid-token, когда токен неверен. Мок различает эти случаи так же: клиент, который по ним отличает «не залогинен» от «ключ протух», в песочнице увидит ту же разницу.

Заголовки: чем Apify отличается от остальных

Здесь три отличия, и каждое снято с живых ответов api.apify.com, а не выведено из документации.

Content-Type приходит с charset — application/json; charset=utf-8, как у Битрикс24, а не голый application/json, как у Ozon и Wildberries.

Из заголовков лимита есть только один. X-RateLimit-Limit приходит на каждом ответе, а -Remaining и -Reset не приходят никогда. Это третий вариант поведения, которого до Apify в стенде не было: сервис сообщает потолок, но не остаток. Клиент, написанный на чтении остатка, в бою получит undefined — и в моке получит его же.

Идентификатора запроса нет. Ни X-Request-Id, ни аналога: в живых ответах его нет ни под одним именем. Свой X-APIStend-Request-Id приходит всегда, как и у Битрикс24, у которого боевого заголовка тоже не существует.

Живой ответ мока:

HTTP/1.1 200 OK
content-type: application/json; charset=utf-8
x-ratelimit-limit: 60
x-apify-pagination-total: 1247
x-apify-pagination-offset: 0
x-apify-pagination-count: 20
x-apify-pagination-limit: 20
x-apify-pagination-desc: false
x-apistend-request-id: req_0d60cf4173
x-apistend-source: example
x-apistend-cors: native

Заголовки пагинации

Списковые ответы Apify дублируют поля конверта data в заголовки X-Apify-Pagination-Total, -Offset, -Count, -Limit, -Desc. Это не украшение: сам Apify перечисляет их в своём Access-Control-Expose-Headers, то есть читать их из браузера предполагается.

Мок берёт значения из того самого тела, которое отдаёт, — заголовок разойтись с телом не может. У одиночного объекта полей пагинации нет, и заголовков тоже не будет.

CORS здесь боевой

X-APIStend-Cors у Apify приходит со значением native, а не added-by-sandbox. Причина простая: боевой Apify отвечает access-control-allow-origin: * и вызовы из браузера разрешает. Пометить это как «добавлено песочницей» значило бы соврать в обратную сторону — будто в бою из браузера ходить нельзя.

Лимиты зависят от эндпоинта

Единственный сервис в стенде, у которого лимит — свойство не сервиса, а метода:

КлассЛимитЧто входит
Запуски и датасеты400 запросов/с/v2/actor-runs, /v2/datasets, запуски и сборки внутри актора и задачи
Key-value store200 запросов/с/v2/key-value-stores
Профиль пользователя90 запросов/с/v2/users
Всё остальное60 запросов/сбазовый лимит, в том числе карточка актора

Числа 400, 200 и 60 — из документации Apify; 90 в ней нет, оно снято с живого ответа /v2/users/me. Мок ведёт по отдельному ведру на каждый класс, поэтому упереться в лимит на датасетах, не тронув остальные вызовы, здесь можно так же, как в бою.

Глобальный потолок — 250 000 запросов в минуту на пользователя — мок не воспроизводит намеренно: до него не доберётся никакая отладочная нагрузка.

Паузы Apify не подсказывает

Заголовка Retry-After в ответах нет, и в документации он не описан. Его собственные клиенты (apify-client для Python и JS) отступают по своей экспоненте: 500 мс, 1 с, 2 с, 4 с, до четырёх попыток. Мок подделывать чужой заголовок не станет — рекомендуемая пауза приходит в X-APIStend-Retry-After.

Конверт ошибки

Один на все коды, из components.schemas.ErrorResponse:

{"error":{"type":"record-not-found","message":"The requested resource was not found."}}
КодtypeКогда
401token-not-providedтокена нет
401invalid-tokenтокен неверен или отозван
404record-not-foundнет такого метода или ресурса
429rate-limit-exceededпревышен лимит
500internal-errorсценарий server_error

Вебхуки

Политика Apify не похожа ни на одну из трёх остальных:

ЧтоЗначение
Тело{userId, createdAt, eventType, eventData, resource}
Успехлюбой код 2xx
Таймаут2 минуты
Повторы11 попыток, интервал удваивается: 1 мин, 2, 4, … до ~32 часов
Подписьнет — вместо неё секрет в самом адресе вебхука

eventData и resource намеренно дублируют друг друга: документация Apify называет это обратной совместимостью. resource — это то, что вернул бы соответствующий метод API в момент события, поэтому шаблон вида {{resource.status}} в песочнице разворачивается так же, как в бою.

Заголовки X-Apify-Webhook, X-Apify-Webhook-Dispatch-Id и X-Apify-Request-Origin система выставляет сама и перезаписывает пользовательские — мок делает то же.

События: ACTOR.RUN.CREATED, .SUCCEEDED, .FAILED, .ABORTED, .TIMED_OUT, а также ACTOR.BUILD.SUCCEEDED и .FAILED.

Доставка может повториться

Документация Apify прямо предупреждает: «In rare cases, the webhook might be invoked more than once». Обработчик обязан быть идемпотентным, и проверить это удобнее на песочнице, чем на боевом аккаунте.

Акторы: ввод и вывод

Мок REST отвечает на «покажи список акторов». Но агент работает не с методами, а с конкретными акторами: читает их схему входа, собирает по ней вызов, запускает и разбирает поля датасета. Поэтому в стенде лежит снимок магазина со схемами входа и выхода настоящих акторов.

ЧтоЗначение
Снято1200 акторов
Со схемой входа1188
С описанием выхода1031
Источник схемactorDefinition последней сборки актора
Размер3,3 МБ в actors.json.gz (12,9 МБ до сжатия)

Выборка складывается из двух частей, и доли зарезервированы, а не остаточны:

  • мировой топ по популярности — 40 % мест. Ими написана большая часть чужого кода, который приносят на отладку;
  • адресный поиск по площадкам, ради которых существует APIStend — Ozon, Wildberries, Яндекс, Avito, VK, Telegram, auto.ru, drom, cian, hh.ru и остальной русский рынок. Таких акторов в снимке 424.

Резерв нужен именно так. В первой версии адресный поиск шёл первым и выбирал всю квоту целиком, вытеснив даже apify/instagram-scraper — самого запускаемого актора магазина.

Весь магазин не снимается намеренно: в нём 57 тысяч акторов, а интеграции пишут с единицами. Количество задаётся аргументом:

node scripts/vendor-apify-actors.mjs 2000

# с токеном — тот же снимок, но выше лимит запросов к Apify
APIFY_TOKEN=… node scripts/vendor-apify-actors.mjs 2000

Токен необязателен: карточки акторов, их сборки и магазин у публичных акторов открыты и анонимно — снимается ровно то, что видит любой посетитель apify.com.

Снимок опционален: без него мок REST работает полностью, а инструменты MCP, которым нужны акторы, честно скажут, что снимка нет, — вместо того чтобы выдумать акторов.