Разработчикам: начало работы
Обновлено 27.10.2026
У ВИШ открытый HTTP API: им пользуется само приложение, и им же могут пользоваться ваши скрипты, боты и сайты. Расписание, группы, преподаватели и аудитории читаются без всякого ключа; ключ нужен, чтобы действовать от своего имени, а «Вход через ВИШ» — чтобы читать данные другого человека с его согласия.
Эта страница — с чего начать. Остальные страницы раздела «Разработчикам» разбирают отдельные части API. Сверено со стендом d1 и production 23 октября 2026 года.
Адреса стендов
| Стенд | Базовый адрес API | Документация OpenAPI |
|---|---|---|
| d1 (тестовый) | https://d1.vish-app.ru/api/v1 | https://d1.vish-app.ru/documentation |
| production | https://api.vish-app.ru/api/v1 (или https://www.vish-app.ru/api/v1) | https://www.vish-app.ru/documentation |
- d1 — тестовый стенд: новые функции появляются здесь раньше, данные могут меняться. Начинайте с него.
- production — то, чем пользуются студенты. Новое попадает сюда релизом раз в две недели.
- Адрес без
www(https://vish-app.ru) отвечает перенаправлением 301 — указывайте полный адрес, иначе часть библиотек потеряет заголовки при переходе. - Машинное описание всех методов —
/documentation/jsonна том же стенде (OpenAPI 3).
Во всех примерах раздела адрес берётся из переменной окружения VISH_API (по умолчанию d1), а ключ — из VISH_API_KEY. Не вписывайте ключ в код.
Первый запрос
Ключ не нужен: список институтов и групп открыт всем.
// List institutes and the first groups; no key needed.
const API = process.env.VISH_API ?? 'https://d1.vish-app.ru/api/v1'
const get = async (path) => {
const response = await fetch(`${API}${path}`, { headers: { Accept: 'application/json' } })
if (!response.ok) throw new Error(`${path}: HTTP ${response.status}`)
return response.json()
}
const institutes = await get('/institutes')
if (!Array.isArray(institutes.list) || institutes.list.length === 0) throw new Error('no institutes')
const groups = await get('/groups?perPage=3')
if (groups.list.length === 0 || typeof groups.meta?.total !== 'number') throw new Error('unexpected /groups')
console.log(`${institutes.list.length} institutes, ${groups.meta.total} groups`)
for (const group of groups.list) console.log(group.name, '—', group.institute?.code)Попробовать прямо здесь:
Попробовать запрос
Ключ нужен только методам с замком. Он живёт только на этой странице: не сохраняется, уходит лишь заголовком этого запроса и забывается, когда вы её покинете. Где взять ключ
Как устроены ответы
- Всё — JSON в UTF-8. Даты — ISO 8601 в UTC (
2026-10-19T13:45:00.000Z). - Списки приходят как
{ "list": [...], "meta": { "total", "page", "perPage", "pages" } }. Страница задаётсяpage(с 1) иperPage(по умолчанию 20, не больше 200). У списков естьall=1— всё одной страницей, но не больше 2000 записей. - Названия, которые переводятся, приходят объектом
{ "ru": "…", "en": "…", "zh": "…" }; русский есть всегда, перевод может отсутствовать. - Новые поля в ответах появляются без предупреждения — не падайте на незнакомых полях.
Ключ доступа
Для чтения открытых данных ключ не нужен. Он нужен, когда скрипт действует от вашего имени: выгружает отчёт, к которому у вашей роли есть доступ, или пишет заметки к занятиям. Как получить ключ, написано на странице API и ключи доступа; как им пользоваться и чем он отличается от OAuth — на странице Аутентификация: ключи API и OAuth.
Формат ошибок
Любая ошибка API — один и тот же объект:
{
"error": {
"code": "AUTH_REQUIRED",
"message": "Нужно войти: передайте заголовок Authorization с Bearer-токеном или заголовок X-Api-Key.",
"requestId": "e47fc5d6-a210-49f2-b480-1384633d1d9f"
}
}code— машинный код, на него и опирайтесь. Он не меняется от языка.message— объяснение для человека на языке из заголовкаAccept-Language(ru,enилиzh; по умолчанию русский). Текст может меняться — не разбирайте его.requestId— номер запроса; тот же номер приходит в заголовкеX-Request-Id. Назовите его, когда пишете о проблеме.details— бывает у ошибок проверки (VALIDATION_ERROR):details.issues— список полей и что с ними не так.
Самые частые коды:
| HTTP | code | Что значит |
|---|---|---|
| 400 | VALIDATION_ERROR, BAD_REQUEST | Неверный параметр или тело запроса |
| 401 | AUTH_REQUIRED | Метод требует входа, а ни ключа, ни токена нет |
| 401 | UNAUTHORIZED | Ключ неверный, отозван или просрочен |
| 401 | ACCESS_TOKEN_INVALID | Bearer-токен недействителен или истёк |
| 403 | FORBIDDEN | Не хватает прав; в сообщении — каких именно |
| 403 | SESSION_REQUIRED | Метод только для человека в приложении, ключом его не вызвать |
| 404 | NOT_FOUND, ROUTE_NOT_FOUND | Нет такой записи или такого адреса |
| 429 | RATE_LIMITED | Слишком много запросов, подождите |
| 500 | INTERNAL_ERROR | Ошибка на нашей стороне |
Подробнее про 403 — на странице Права доступа (RBAC). Протокольные адреса OAuth (/oauth/token, /oauth/userinfo и другие) отвечают по стандарту OAuth: { "error": "invalid_grant", "error_description": "…" }.
Посмотреть ошибку целиком — запросить без ключа метод, который требует входа:
// /auth/me needs a credential: without one the answer shows the error format.
const API = process.env.VISH_API ?? 'https://d1.vish-app.ru/api/v1'
const response = await fetch(`${API}/auth/me`, { headers: { 'Accept-Language': 'en' } })
const body = await response.json()
if (response.status !== 401 || body.error?.code !== 'AUTH_REQUIRED' || !body.error.requestId) {
throw new Error(`unexpected answer: ${response.status} ${JSON.stringify(body)}`)
}
console.log(body.error.code, '-', body.error.message)
console.log('requestId:', body.error.requestId)Ограничение частоты запросов
- Общий лимит — 300 запросов в минуту на вошедшего человека или, без входа, на IP-адрес.
- У каждого ключа — свой лимит, по умолчанию 3000 запросов в минуту. Выдуманный ключ лимит не увеличивает: такие запросы считаются по адресу.
- У тяжёлых методов лимиты свои и строже: календарная лента — 60 в минуту, сводки за период (загруженность, отчёты) и обмен кода OAuth на токен — 30 в минуту, PDF — 10 в минуту.
- Превышение — ответ 429 с
error.codeRATE_LIMITED; в сообщении сказано, через сколько секунд повторить. На production ответы несут заголовкиRateLimit-Limit,RateLimit-RemainingиRateLimit-Reset, по ним можно притормозить заранее (на d1 23 октября 2026 года их не было).
Версии API
Все адреса начинаются с /api/v1. Внутри v1 API меняется только с сохранением совместимости: добавляются методы, параметры и поля ответов. Если что-то придётся изменить несовместимо, это будет новая версия по новому адресу, а о планах заранее напишем на странице Изменения API.
Что дальше
- Аутентификация: ключи API и OAuth
- Вход через ВИШ для разработчиков
- Права доступа (RBAC)
- API расписания и API справочников
- API заметок и Уведомления и календари
- Безопасность интеграций, Изменения API, Вопросы разработчиков
Проверено: 2026-10, PWA aa337c45, platform bff65fce.