Документация
Адрес API: https://api.fuck-api.xyz. Фильтры каждой площадки и сборщик скрипта на страницах площадок.
Ключ
Ключ выдаёт @fuckparserbot, кнопка «🔑 API». Он нужен в заголовке X-API-Key везде, кроме GET /v1/health. «♻️ Перевыпустить ключ» сразу выключает старый.
HEADERS = {"X-API-Key": "fp_ВАШ_КЛЮЧ"}
Задача
POST /v1/tasksтолько создаёт задачу и возвращает еёtask.id. Объявлений в этом ответе нет. Повтор с теми же фильтрами, пока задача жива, вернёт тот жеid.GET /v1/tasks/{id}/adsотдаёт объявления: только новые с прошлого запроса. Новую задачу не запускает.- Ответ потерялся: повтори с
repeat=1, придёт та же порция. - Задача живёт 30 минут или до
limitобъявлений. - Нет
GETк задаче 3 минуты: она останавливается сама, на случай если скрипт упал. - Закончилась:
task.statusстанетdone, причина вtask.reason. Забирай остаток, покаadsне придёт пустым. - Задача одна за раз: другая получит 409
task_running, номер идущей вerror.task. Остановить:POST /v1/stopилиDELETE /v1/tasks/{id}. - Без подписки задача не стартует, придёт
notice. - Память с ботом общая: что ты получил в боте, через API не придёт, и наоборот.
status | Что значит |
|---|---|
starting | Поиск запускается. |
running | Поиск идёт. |
done | Задача закончилась, причина в reason. |
reason | Почему закончилась |
|---|---|
limit | Набрано limit объявлений. |
time | Вышло время. |
idle | Не было GET к задаче. |
stopped | Остановлена: POST /v1/stop, DELETE /v1/tasks/{id} или «⏹ Остановить задачу» в боте. |
panel | Остановлена в панели. |
failed | Сбой сервера или площадка не ответила. |
interrupted | Бот перезапускался. Остаток можно забрать. |
refused | Не стартовала: нет подписки, бан или слишком много разделов. |
banned | API закрыт. |
off | API выключен. |
Параметры
| Параметр | Где | Что это | Если не задать |
|---|---|---|---|
platform | тело POST /v1/tasks | Площадка из GET /v1/platforms. | нужен всегда |
filters | тело POST /v1/tasks | Фильтры площадки. | умолчания площадки |
limit | тело POST /v1/tasks | Объявлений за задачу, от 1 до 1000. | 1000 |
wait | GET /v1/tasks/{id}/ads | Сколько секунд ждать новое, от 0 до 25. | 0 |
repeat | GET /v1/tasks/{id}/ads | 1: прошлая порция ещё раз. | нет |
Другие поля и параметры, неизвестный фильтр, вариант не из списка, число вне пределов, фильтр чужой страны, limit больше 1000: ответ 400, поле в error.field. Пустое значение ("", null, []) значит «фильтра нет». Страну (gate: true в схеме) задавай первой, фильтр с countries работает только в этих странах.
Адреса
| Запрос | Что делает |
|---|---|
POST /v1/tasks | Создать и запустить задачу: {"task": {...}}. |
GET /v1/tasks/{id}/ads | Новое с прошлого запроса: {"task": {...}, "ads": [...]}. |
GET /v1/tasks/{id} | Одна задача. |
DELETE /v1/tasks/{id} | Остановить эту задачу. |
POST /v1/stop | Остановить идущую задачу: {"stopped": [...]}. |
GET /v1/tasks | Твои задачи за 3 дня: {"tasks": [...]}. |
GET /v1/platforms | Площадки и их страны. |
GET /v1/platforms/{key} | Фильтры площадки: вид, варианты, пределы, примеры. |
GET /v1/platforms/{key}/filters/{name}?parent= | Подразделы дерева. Без parent верхние разделы. |
GET /v1/platforms/{key}/places?q= | Код города по названию, от двух букв. Страну передай рядом. |
GET /v1/me | Начало ключа, доступ, лимиты, идущая задача. |
GET /v1/health | Жив ли API, без ключа. |
GET /v1/adsPOST /v1/ads | Старый способ одним запросом. |
Значения фильтров
Вид (kind) | Как писать | Пример |
|---|---|---|
| Один вариант | строка с кодом | "published": "1h" |
| Несколько вариантов | список кодов | "condition": ["new", "used"] |
| Разделы | код или список кодов | "category": ["1307", "1312"] |
| Диапазон | [от, до] или строка «от-до» | "price": [100, 500] |
| Число | число | "seller_ads": 5 |
| Да или нет | true или false | "with_phone": true |
| Текст | строка | "query": "iphone 13" |
| Место | строка с кодом места | "location": "3331" |
- Диапазон:
100-от 100,-500до 500,0-0ровно ноль. Одно число без дефиса фильтры читают по-разному, смотри пример у фильтра. - Разделы открываются по веткам: без
parentверхние, дальше код раздела сmore: true. Адрес в схеме с?parent={country}: первым идёт код страны. - Место пишется кодом из
GET /v1/platforms/{key}/places?q=. - В тексте лишние пробелы схлопываются, длина до
max_length.
Ответ
Ответ GET /v1/tasks/{id}/ads: задача и новые объявления. Время в UTC.
{
"task": {
"id": "t_k3v9q2m7xw4p",
"platform": "gumtree",
"status": "running",
"found": 1,
"handed": 1,
"limit": 200,
"started_at": "2026-10-20T20:49:52Z",
"ends_at": "2026-10-20T21:19:52Z"
},
"ads": [
{
"id": "2384712650",
"title": "iPhone 13 128 GB",
"price": 340,
"currency": "EUR",
"price_type": "Fixed",
"url": "https://www.gumtree.com/ad/2384712650",
"location": "Berlin",
"published_at": "2026-10-20T20:46:06Z",
"seller_name": "Marek",
"seller_type": "Private",
"seller_ads": 2,
"seller_sales": 0,
"phone": "+4915100000000",
"whatsapp": "https://wa.me/4915100000000",
"condition": "Used",
"seen_by": 0
}
]
}
Без подписки
POST /v1/tasks отвечает HTTP 200, task равен null, причина в notice. Подписка открывает все площадки, начатая задача доживает срок, даже если подписка кончилась.
{
"task": null,
"notice": {
"code": "no_subscription",
"message": "No subscription."
}
}
code | message | Что делать |
|---|---|---|
no_subscription | No subscription. | Оформи подписку в боте. |
no_subscription | Subscription expired. | Продли подписку в боте. |
Задача
| Поле | Что это |
|---|---|
id | t_ и 12 знаков. |
platform | Площадка. |
status | starting, running или done. |
reason | Почему закончилась, только у done. |
found | Сколько нашла. |
handed | Сколько ты забрал. |
limit | Потолок объявлений. |
started_at | Когда запустилась. |
ends_at | Когда закончится, у законченной: когда закончилась. |
POST /v1/tasks, GET /v1/tasks/{id}, DELETE /v1/tasks/{id} и POST /v1/stop добавляют filters, asked_at и ended_at.
Объявление
Пустых полей нет: нет телефона, нет и phone. Порядок в порции: в каком нашли.
| Поле | Тип | Что это |
|---|---|---|
id | строка | Номер на площадке, есть всегда. |
title | строка | Заголовок. |
price | число | Цена в валюте объявления. |
currency | строка | EUR, PLN, USD и другие. |
price_type | строка | Fixed, Negotiable, On request, Free, Swap, Reserved, Auction, Auction/Buy now. |
url | строка | Объявление. |
chat_url | строка | Чат с продавцом, если у площадки свой чат. |
category | строка | Раздел, например Electronics / Mobile Phones. |
location | строка | Место, как на площадке. |
published_at | строка | Когда вышло, ISO 8601, UTC. |
seller_name | строка | Имя продавца. |
seller_type | строка | Private, Commercial, Business, Dealer, Shop, PRO shop. |
seller_id | строка | Номер продавца на площадке. |
seller_url | строка | Профиль продавца. |
seller_ads | число | Объявлений у продавца сейчас. |
seller_total_ads | число | Объявлений у продавца всего. |
seller_sales | число | Продаж у продавца. 0 значит ноль, а не «неизвестно». |
seller_purchases | число | Покупок у продавца. |
seller_sold_items | число | Счётчик проданного у Wallapop, не равен seller_sales. |
seller_reviews | число | Отзывов у продавца. |
seller_rating | строка | Оценка, как у площадки: 88% или 4.7/5. |
seller_registered_at | строка | Когда продавец завёл аккаунт, ISO 8601, у части площадок только YYYY-MM или YYYY. |
seller_reply_rate | строка | Как часто отвечает, например 81%. |
seller_followers | число | Подписчиков у продавца. |
seller_city | строка | Город продавца, если площадка отдаёт его отдельно. |
phone | строка | Телефон продавца, как на площадке. |
whatsapp | строка | Ссылка wa.me, когда есть номер. |
possible_email | строка | Почта, угаданная по нику, у площадок с проверкой почты. |
email_verified | true или false | true, если Gmail подтвердил ящик. |
condition | строка | New, Like new, Used, Refurbished, Good, Not working, набор у площадок свой. |
shipping | true или false | Есть доставка. |
buy_now | true или false | Можно купить сразу. |
promoted | true или false | Продавец платил за место в ленте. |
image | строка | Главное фото. |
description | строка | Текст объявления. |
seen_by | число | Скольким другим людям оно уже пришло. |
Свои поля некоторых площадок (brand, model, year, mileage) приходят как есть.
Ошибки
Код 4xx или 5xx, field есть, когда виновато поле. no_subscription не ошибка, а 200 с notice.
{
"error": {
"code": "invalid_filter",
"message": "price: must be from 0 to 100000000.",
"field": "price"
}
}
| HTTP | code | Когда | Что делать |
|---|---|---|---|
| 400 | bad_request | Тело не JSON, лишнее поле или параметр, limit или wait вне пределов, в q меньше двух букв. | Поле в error.field. |
| 400 | unknown_platform | Нет такой площадки, или она закрыта. | GET /v1/platforms. |
| 400 | unknown_filter | У площадки нет такого фильтра или дерева. | Фильтры на странице площадки. |
| 400 | invalid_filter | Нет такого варианта, число вне пределов, фильтр чужой страны, много разделов. | Фильтр в error.field. |
| 400 | place_unknown | Код места не найден. | Код из GET /v1/platforms/{key}/places?q=. |
| 401 | no_key | Нет X-API-Key. | Добавь ключ. |
| 401 | bad_key | Ключ неверный или перевыпущен. | Свежий ключ в боте. 10 неверных за 10 минут закрывают адрес на час. |
| 403 | banned | API закрыт. | Поддержка бота. |
| 404 | not_found | Нет такой задачи (чужая тоже 404) или адреса. | Свои задачи: GET /v1/tasks. |
| 405 | bad_method | Метод не подходит к адресу. | Таблица адресов. |
| 409 | task_running | Задач уже идёт столько, сколько можно разом. Номер идущей в error.task. | POST /v1/stop или те же фильтры. |
| 409 | busy_task | Задачу сейчас читает другой запрос. | Повтори через секунду. |
| 413 | too_large | Тело больше 16 КБ. | Меньше фильтров. |
| 429 | rate_limited | Много запросов с ключа или адреса, или частый поиск города. | Жди Retry-After секунд. |
| 429 | slow_down | Много запросов ключа разом, частый запуск задач, или адрес закрыт за перебор ключей. | Жди Retry-After секунд. |
| 500 | internal | Сбой сервера. | Повтори с repeat=1. Не проходит: X-Request-Id в поддержку. |
| 503 | busy | Сервер перегружен. | Жди Retry-After секунд. |
| 503 | updating | Бот перезапускается, найденное можно забирать. | Жди Retry-After секунд. |
| 503 | unavailable | API выключен, или площадка не ответила на поиск города. | Жди Retry-After секунд. |
Заголовки: X-Request-Id в каждом ответе, X-RateLimit-Limit, X-RateLimit-Remaining и X-RateLimit-Reset в ответах с ключом, Retry-After в 429 и 503. Больше 50 запросов за 10 секунд с адреса режет Cloudflare: 429 с HTML вместо JSON.
Лимиты
| Что | Сколько |
|---|---|
| Запросов в минуту на ключ | 60, подряд до 20 |
| Запросов ключа одновременно | 3 |
| Задач одновременно | 1 |
| Жизнь задачи | 30 минут или limit объявлений |
Нет GET к задаче | конец через 3 минуты |
limit | до 1000 |
wait | до 25 секунд |
| Поиск города | 10 в минуту |
| Тело запроса | 16 КБ |
| Неверный ключ | 10 за 10 минут закрывают адрес на час |
Скрипт
Тот же, что собирает страница площадки: создаёт задачу, забирает новое, повторяет потерянный ответ, ждёт Retry-After, выходит на done и notice.
import time
import requests
API = "https://api.fuck-api.xyz"
HEADERS = {"X-API-Key": "fp_ВАШ_КЛЮЧ"}
BODY = {
"platform": "gumtree",
"filters": {
"query": "iphone 13",
"price": [100, 500],
"with_phone": True,
},
"limit": 200,
}
def show(ad):
print(ad.get("title"), ad.get("price"), ad.get("currency", ""), ad.get("phone", "без телефона"), ad.get("url"))
def ask(method, path, **given):
while True:
try:
response = requests.request(method, f"{API}{path}", headers=HEADERS, timeout=40, **given)
except requests.RequestException:
return None
if response.status_code not in (429, 503):
return response
time.sleep(int(response.headers.get("Retry-After", 5)))
def lost(response):
return response is None or response.status_code >= 500
def main():
response = ask("POST", "/v1/tasks", json=BODY)
while lost(response):
time.sleep(5)
response = ask("POST", "/v1/tasks", json=BODY)
answer = response.json()
if response.status_code != 200:
print(answer["error"]["code"], answer["error"]["message"])
return
if "notice" in answer:
print(answer["notice"]["message"])
return
path = f"/v1/tasks/{answer['task']['id']}/ads"
repeat = False
while True:
response = ask("GET", path, params={"wait": 20, "repeat": 1} if repeat else {"wait": 20})
if lost(response):
repeat = True
time.sleep(5)
continue
answer = response.json()
if response.status_code != 200:
print(answer["error"]["code"], answer["error"]["message"])
return
repeat = False
for ad in answer["ads"]:
show(ad)
if answer["task"]["status"] == "done" and not answer["ads"]:
print("Задача закончилась:", answer["task"].get("reason"))
return
time.sleep(5)
main()
Остановить задачу и посмотреть свои
import requests
API = "https://api.fuck-api.xyz"
HEADERS = {"X-API-Key": "fp_ВАШ_КЛЮЧ"}
print(requests.post(f"{API}/v1/stop", headers=HEADERS, timeout=30).json())
print(requests.get(f"{API}/v1/tasks", headers=HEADERS, timeout=30).json())
print(requests.get(f"{API}/v1/me", headers=HEADERS, timeout=30).json())
Найти код города
import requests
API = "https://api.fuck-api.xyz"
HEADERS = {"X-API-Key": "fp_ВАШ_КЛЮЧ"}
PARAMS = {
"q": "Berlin",
}
answer = requests.get(f"{API}/v1/platforms/kleinanzeigen/places", params=PARAMS, headers=HEADERS, timeout=40).json()
if "error" in answer:
print(answer["error"]["message"])
for place in answer.get("places", []):
print(place["id"], place["name"], place.get("region", ""))
{"places": [{"id": "3331", "name": "Berlin", "region": "Berlin"}]}