Skip to content
Unixgram

The API reference is currently available in Russian only. Method names, paths and examples are the same in every language.

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.