Перейти к содержимому
Unixgram

Client API

HTTP API, на котором работают сами приложения Unixgram: веб, Android и iOS. С ним можно написать собственный клиент, бота от имени своего аккаунта, экспорт переписки или любую интеграцию. Ключ разработчика не нужен — нужен аккаунт.
Содержание

Обзор#

  • Адрес: https://unixgram.com/api. Все запросы и ответы — JSON в UTF-8, кроме загрузки файлов.
  • Авторизация — cookie сессии, как у браузера. Любая HTTP-библиотека с хранилищем cookie (requests.Session, curl -c/-b, OkHttp CookieJar) работает без доработок.
  • Каждый запрос, который что-то меняет (POST, PUT, PATCH, DELETE), несёт заголовок x-csrf-token — как его получить.
  • Полный список — 349 методов с параметрами — в справочнике методов. События в реальном времени — в Realtime.
  • Программа должна работать на сервере, компьютере или телефоне. Страница в браузере на другом сайте обратиться к API не сможет: cookie сессии выдаются с SameSite=Lax, а CORS-заголовков API не отдаёт. Для сайта используйте свой сервер как посредника — или бота и мини-приложение.

Быстрый старт#

Python и requests: войти, прочитать ленту и написать человеку.

python
import uuid, requests

API = "https://unixgram.com/api"
s = requests.Session()
s.headers["User-Agent"] = "my-client/1.0"

def csrf():
    return s.get(f"{API}/auth/csrf").json()["data"]["csrfToken"]

def call(method, path, **json):
    headers = {} if method == "GET" else {"x-csrf-token": s.cookies.get("csrf_token") or csrf()}
    r = s.request(method, API + path, json=json or None, headers=headers)
    body = r.json()
    if not body["success"]:
        raise RuntimeError(body["error"])
    return body["data"]

csrf()
me = call("POST", "/auth/login", email="me@example.com", password="••••••••")["account"]
print("Вошли как", me["username"])

for post in call("GET", "/social/feed?limit=10")["feed"]:
    print(post["author"]["username"], post["content"][:60])

chat = call("POST", "/social/users/unixgram/message")       # личный чат по нику
call("POST", f"/social/messages/{chat['conversationId']}",
     content="Привет из API!", clientMessageId=str(uuid.uuid4()))

То же на curl:

shell
# 1. CSRF-токен и cookie
TOKEN=$(curl -s -c jar -b jar https://unixgram.com/api/auth/csrf | jq -r .data.csrfToken)

# 2. Вход
curl -s -c jar -b jar -H "x-csrf-token: $TOKEN" -H "content-type: application/json" \
  -d '{"email":"me@example.com","password":"••••••••"}' https://unixgram.com/api/auth/login

# 3. Список чатов
curl -s -b jar https://unixgram.com/api/social/messages

Вход и сессия#

Вход#

POST /api/auth/login с телом {"email", "password", "twoFactorCode"?}. Ответ — {account} и две cookie сессии. Если у аккаунта включена двухфакторная защита, первый вызов вернёт ошибку TWO_FACTOR_REQUIRED (или …_EMAIL_REQUIRED, …_TELEGRAM_REQUIRED — код отправлен туда) — повторите вход с twoFactorCode из шести цифр.

Аккаунт для программы создаётся обычной регистрацией в приложении: регистрация защищена капчей, которую сторонний клиент пройти не может. Вход капчу не спрашивает.

Сессия#

ПолеТипОписание
auth_access_tokencookie, httpOnlyДоступ на 15 минут
auth_refresh_tokencookie, httpOnlyПродление на 30 дней
csrf_tokencookieДвойник заголовка x-csrf-token

Продлевать сессию вручную не нужно: если access-токен истёк, а refresh жив, сервер выдаст новые cookie в ответ на любой запрос. Ошибка UNAUTHORIZED значит, что истёк и refresh, — войдите заново. Текущий аккаунт — GET /api/auth/me, выход — POST /api/auth/logout, список устройств — GET /api/auth/sessions.

Вход по QR#

Чтобы не хранить пароль в программе, войдите так же, как веб-версия входит по QR-коду с телефона:

  1. POST /api/auth/qr/start → {"token", "expiresAt", "pollIntervalMs"}. Покажите QR-код со ссылкой https://unixgram.com/auth/link?t=<token>.
  2. Человек сканирует его приложением Unixgram, в котором уже вошёл, и подтверждает вход.
  3. Программа опрашивает GET /api/auth/qr/status?token=… (раз в pollIntervalMs); при APPROVED — вызывает POST /api/auth/qr/claim с {"token"} и получает cookie сессии.

CSRF#

GET /api/auth/csrf возвращает {"csrfToken": "…", "captchaRequired": false} и ставит cookie csrf_token с тем же значением. Передавайте его в заголовке x-csrf-token в каждом изменяющем запросе. Токен не одноразовый — берите его из cookie, пока она есть.

http
POST /api/social/posts HTTP/1.1
Host: unixgram.com
Cookie: auth_access_token=…; auth_refresh_token=…; csrf_token=Rk9…f0.9c1…
x-csrf-token: Rk9…f0.9c1…
content-type: application/json

{"content": "Первый пост из API"}

Ответы и ошибки#

Любой ответ — один конверт. Разбирайте по полю success, а не по коду состояния:

json
{"success": true, "data": {"post": {"id": "cmh1x…", "content": "…"}}}

{"success": false, "error": {"code": "VALIDATION_ERROR", "message": "Validation failed",
  "details": {"fieldErrors": {"content": ["Too big: expected string to have <=2000 characters"]}}}}
error.codeHTTPЧто значит
VALIDATION_ERROR400 · 422Неверные параметры. В details — какое поле и почему (422 — ошибка схемы)
UNAUTHORIZED401Сессии нет или refresh-токен истёк — войдите заново
CSRF_TOKEN_INVALID403Нет заголовка x-csrf-token или он не совпадает с cookie
FORBIDDEN · PERMISSION_DENIED403Действие запрещено этому аккаунту
NOT_FOUND404Нет такого объекта или он вам недоступен
RATE_LIMITED429Слишком часто — подождите (заголовок Retry-After)
INVALID_CREDENTIALS401Неверная почта или пароль
EMAIL_NOT_VERIFIED403Почта не подтверждена
TWO_FACTOR_REQUIRED401Нужен код двухфакторной аутентификации (twoFactorCode)
TWO_FACTOR_EMAIL_REQUIRED401Код отправлен на почту — повторите вход с ним
TWO_FACTOR_TELEGRAM_REQUIRED401Код отправлен в Telegram-бот @unixgram_auth
TWO_FACTOR_INVALID_CODE401Неверный код
ACCOUNT_DISABLED · DEVICE_BANNED403Аккаунт или устройство заблокированы
CAPTCHA_REQUIRED · CAPTCHA_FAILED400Нужна капча (регистрация)
POSTING_TEMPORARILY_BLOCKED403Публикация временно ограничена
CONTENT_REJECTED422Содержимое отклонено модерацией
INTERNAL_ERROR500Ошибка сервера — повторите позже

Идентификаторы объектов — строки (cuid), время — ISO 8601 в UTC.

Постраничная выдача#

Списки отдаются страницами с курсором: в ответе — элементы и курсор следующей страницы (pageInfo.nextCursor, nextCursor или pageInfo.oldestCursor); передайте его в ?cursor= (в истории сообщений — ?before=). Курсора нет — список закончился. Размер страницы — ?limit=, где метод его принимает.

Загрузка файлов#

Файл сначала загружается, потом его адрес передаётся в сообщение, пост или профиль. POST /api/account/upload, multipart/form-data с полями file и kind; ответ — {"url", "thumb", "posterUrl"}.

python
up = s.post(f"{API}/account/upload", files={"file": open("cat.jpg", "rb")},
            data={"kind": "chat-image"}, headers={"x-csrf-token": s.cookies["csrf_token"]}).json()["data"]
call("POST", f"/social/messages/{chat_id}", clientMessageId=str(uuid.uuid4()),
     media={"type": "image", "url": up["url"]})
kindПределДля чего
chat-image5 МБИзображение в чат
chat-video25 МБВидео или кружок в чат
chat-voice12 МБГолосовое в чат
chat-file50 МБЛюбой файл в чат
post5 МБИзображение к посту
post-video120 МБВидео к посту
post-file50 МБФайл к посту
story · story-video · story-voice5 · 120 · 12 МБИстория
comment-image · comment-video · comment-voice5 · 25 · 12 МБВложение в комментарий
avatar · cover · cover-video5 · 5 · 25 МБАватар и обложка профиля

Вложение сообщения — media: {type: "image"|"video"|"voice"|"file"|"sticker", url, name?, mime?, sizeBytes?, width?, height?, durationMs?, waveform?}. Кружок — type: "video" с name: "circle".

Повторы без дублей#

Сеть рвётся, запросы повторяются. Отправка сообщения принимает clientMessageId, публикация поста — clientPostId: случайная строка до 64 символов, одна на одно действие. Повтор с тем же ключом вернёт уже созданный объект, а не второй такой же.

Лимиты#

  • Не больше 20 запросов в секунду с одного IP-адреса на всё API.
  • Вход и восстановление пароля — 10 попыток за 10 минут с адреса; отправка кодов — строже.
  • Публикации, комментарии и подписки ограничены по частоте на аккаунт, как в приложении.
  • Превышение — 429 с заголовком Retry-After. Подождите и повторите; не долбите сервер в цикле — адрес попадёт под автоматическую защиту.

Примеры#

ПолеТипОписание
GET /api/social/feed?cursor ?limit ?followingЛента → feed, pageInfo
POST /api/social/postscontent, imageUrls, poll, clientPostId…Опубликовать пост
POST /api/social/posts/{postId}/like—Лайк / снять лайк
GET /api/social/users/{username}—Профиль
POST /api/social/users/{username}/follow—Подписаться / отписаться
GET /api/social/messages—Список чатов
GET /api/social/messages/{conversationId}?before ?limitЧат и сообщения
POST /api/social/messages/{conversationId}content, clientMessageId, replyToId, media…Отправить сообщение
POST /api/social/users/{username}/message—Открыть личный чат → conversationId
GET /api/social/search?q ?type ?cursorПоиск
GET /api/social/notifications—Уведомления

Это малая часть — остальные 338 методов, с полями тел запросов, в справочнике.

Правила#

  • Программа действует от имени вашего аккаунта, и отвечаете за неё вы. Спам, накрутка и массовые рассылки блокируются так же, как у людей.
  • Указывайте осмысленный User-Agent с названием программы — так проще помочь, если что-то пойдёт не так.
  • Методы помечены в справочнике так, как их видит сервер; поля ответа могут дополняться без предупреждения — игнорируйте незнакомые. О несовместимых изменениях сообщаем в @unixgram.