Как подключить внешний API к программе: методы, параметры, заголовки и тело запроса
Внешний API позволяет одной программе обратиться к другой без ручной работы через интерфейс.
Например, программа может:
- получить прогноз погоды;
- создать задачу в CRM;
- отправить сообщение в Telegram;
- проверить статус платежа;
- загрузить документ;
- получить список заказов;
- передать данные из формы в другую систему.
Инструмент может быть разным: Python, n8n, JavaScript, Postman или backend-приложение. Но основа всегда одна:
Программа формирует HTTP-запрос
-
Сервис получает запрос
-
Проверяет адрес, метод, параметры и доступ
-
Выполняет операцию
-
Возвращает HTTP-ответ
Чтобы подключить API, нужно научиться переводить его документацию в рабочий запрос.
HTTP-методы GET, POST, PUT, PATCH, DELETE и QUERY: примеры запросов
В этой статье разберём один и тот же запрос на уровне HTTP, в Python и в n8n. Так будет видно, что меняется только способ настройки, а сам принцип остаётся одинаковым.
Что значит подключить API
API - предусмотренный разработчиками способ взаимодействия с системой из другой программы.
Человек может создать задачу через интерфейс:
Открыть сервис
-
Нажать «Новая задача»
-
Заполнить поля
-
Нажать «Сохранить»
Программа делает то же самое через запрос:
POST /tasks
Content-Type: application/json
{
"title": "Позвонить клиенту",
"priority": "high"
}
Сервер принимает данные, создаёт задачу и возвращает результат:
{
"id": 481,
"title": "Позвонить клиенту",
"priority": "high",
"status": "created"
}
Программа получает ответ и может использовать его дальше: сохранить идентификатор задачи, отправить уведомление или запустить следующий этап автоматизации.
Подключение API состоит не только из отправки запроса. Нужно также понимать:
- куда обращаться;
- какой метод использовать;
- где передавать параметры;
- как подтвердить доступ;
- какой формат данных ожидает сервер;
- что вернётся в ответ;
- как реагировать на ошибки.
Что найти в документации API
Перед настройкой HTTP-узла сначала восстановите структуру запроса по документации сервиса.
Не нужно искать отдельные параметры хаотично. Удобнее двигаться от общей конструкции запроса к её частям:
HTTP-запрос
├── адрес
├── метод
├── заголовки
├── тело
└── ожидаемый ответ
Дополнительно нужно проверить ограничения конкретного API.
1. Адрес запроса
Сначала найдите URL, на который нужно отправить запрос.
Обычно он состоит из нескольких частей:
Базовый URL
+
endpoint
+
path-параметры
+
query-параметры
Например:
https://api.example.com/tasks/481?status=active
Здесь:
https://api.example.com - базовый URL
/tasks/481 - endpoint
481 - path-параметр
status=active - query-параметр
Endpoint определяет конкретную операцию API.
Например:
https://api.example.com/tasks
может относиться ко всей коллекции задач, а:
https://api.example.com/tasks/481
к задаче с идентификатором 481.
В документации нужно проверить:
- полный URL или базовый адрес;
- путь endpoint;
- обязательные path-параметры;
- доступные query-параметры.
2. HTTP-метод
Метод сообщает серверу, какое действие требуется выполнить.
GET - получить данные
POST - создать объект или запустить операцию
PUT - передать полное новое состояние объекта
PATCH - изменить отдельные поля
DELETE - удалить ресурс
Метод нельзя выбирать только по удобству.
Если документация требует:
POST /tasks
запрос с методом GET не станет его заменой, даже если endpoint совпадает.
3. Заголовки запроса
Заголовки передают служебную информацию о запросе.
В них могут находиться:
- данные авторизации;
- формат тела запроса;
- ожидаемый формат ответа;
- версия API;
- идентификатор запроса;
- дополнительные параметры сервиса.
Авторизация
Многие API принимают токен через заголовок Authorization:
Authorization: Bearer TOKEN
Здесь:
Authorization - название заголовка
Bearer - схема авторизации
TOKEN - секретное значение
Другой сервис может ожидать API key в отдельном заголовке:
X-API-Key: TOKEN
Иногда ключ передаётся не в заголовке, а в query-параметре. Точный способ нужно брать из документации API.
Формат тела
Если запрос содержит JSON, обычно используется заголовок:
Content-Type: application/json
Он сообщает серверу, как интерпретировать тело запроса.
Другие возможные значения:
application/json - JSON
application/x-www-form-urlencoded - поля формы
multipart/form-data - форма с файлами
text/plain - обычный текст
Формат ответа
Заголовок Accept может сообщать серверу, какой формат ответа ожидает клиент:
Accept: application/json
Не каждый API требует указывать его вручную, но это нужно проверить в документации.
4. Тело запроса
Тело содержит данные, которые отправляются серверу.
Оно чаще используется с методами:
POST
PUT
PATCH
Например:
{
"title": "Проверить интеграцию",
"priority": "high"
}
В документации нужно определить:
- требуется ли тело;
- в каком формате оно передаётся;
- какие поля обязательны;
- какие поля необязательны;
- какие типы значений ожидаются;
- какие ограничения действуют для каждого поля.
Например:
title - обязательная строка
priority - необязательная строка
completed - логическое значение true или false
Необязательное поле можно пропустить, а отсутствие обязательного обычно приведёт к ошибке.
5. Ответ сервера
HTTP-ответ также состоит из нескольких частей:
HTTP-ответ
├── статус-код
├── заголовки
└── тело
Статус-код
Статус-код показывает результат обработки запроса.
200 OK - запрос выполнен
201 Create - объект создан
204 No Content - операция выполнена, тело ответа отсутствует
400 Bad Request - ошибка в структуре запроса
401 Unauthorized - не пройдена авторизация
403 Forbidden - доступ запрещён
404 Not Found - ресурс не найден
422 Unprocessable Content - данные не прошли проверку
429 Too Many Requests - превышен лимит запросов
500 Internal Server Error - внутренняя ошибка сервера
Тело ответа
Документация должна показывать структуру успешного ответа.
Например:
{
"id": 481,
"title": "Проверить интеграцию",
"status": "created"
}
Также полезно найти примеры ошибочных ответов:
{
"error": "invalid_token",
"message": "Token has expired"
}
Это поможет правильно настроить дальнейшие узлы и обработку ошибок.
6. Ограничения API
Кроме структуры запроса, проверьте эксплуатационные ограничения.
Например:
- максимальное количество запросов;
- максимальный размер тела;
- допустимые форматы файлов;
- срок действия токена;
- обязательные версии API;
- правила пагинации;
- ограничения длины строк;
- допустимые значения полей.
Пример ограничения частоты:
100 запросов в минуту
При превышении сервер может вернуть:
429 Too Many Requests
Итоговая проверка
Перед настройкой узла у вас должна быть заполнена такая схема:
Адрес
-
базовый URL
endpoint
path-параметры
query-параметры
Метод
-
GET, POST, PUT, PATCH или DELETE
Заголовки
-
Authorization
Content-Type
Accept
дополнительные заголовки
Тело
-
формат
обязательные поля
необязательные поля
Ответ
-
успешные статус-коды
ошибочные статус-коды
структура JSON
Ограничения
-
rate limit
размер запроса
срок действия токена
другие требования
После этого параметры из документации можно последовательно перенести в HTTP-узел, не смешивая адрес, заголовки, тело и ответ между собой.
Разбираем один запрос целиком
Рассмотрим создание задачи:
POST https://api.example.com/tasks
Authorization: Bearer TOKEN
Content-Type: application/json
Accept: application/json
{
"title": "Позвонить клиенту",
"priority": "high"
}
Этот запрос можно представить как набор настроек:
Метод: POST
URL: https://api.example.com/tasks
Авторизация: Bearer token
Формат тела: JSON
Ожидаемый ответ: JSON
Поле title: название задачи
Поле priority: приоритет
Именно эти значения нужно перенести в Python, n8n или другой инструмент.
Документация иногда показывает запрос в виде команды curl:
curl -X POST "https://api.example.com/tasks" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Позвонить клиенту","priority":"high"}'
curl - консольная программа для отправки HTTP-запросов. Такая команда может выглядеть непривычно, но она содержит те же элементы: метод, адрес, заголовки и тело.
Первый GET-запрос
Для практики используем JSONPlaceholder - тестовый API с ненастоящими публикациями, пользователями и комментариями.
Получим публикацию с идентификатором 1:
GET https://jsonplaceholder.typicode.com/posts/1
Сервер возвращает объект:
{
"userId": 1,
"id": 1,
"title": "Заголовок публикации",
"body": "Текст публикации"
}
Используется GET, потому что запрос только получает данные.
GET-запрос в Python
Установите HTTPX:
pip install httpx
Код:
import httpx
url = "https://jsonplaceholder.typicode.com/posts/1"
response = httpx.get(url, timeout=10.0)
response.raise_for_status()
post = response.json()
print(post["id"])
print(post["title"])
Разберём последовательность:
httpx.get(...) - отправляет GET-запрос
timeout=10.0 - ограничивает ожидание ответа
raise_for_status() - создаёт исключение при ошибочном статусе
response.json() - преобразует JSON-ответ в объект Python
После response.json() JSON-объект становится словарём Python:
{
"userId": 1,
"id": 1,
"title": "Заголовок публикации",
"body": "Текст публикации",
}
Поэтому к значению можно обратиться через ключ:
post["title"]
Тот же запрос в n8n
Добавьте узел HTTP Request и укажите:
Method: GET
URL: https://jsonplaceholder.typicode.com/posts/1
После выполнения ответ станет результатом узла.
Следующий узел сможет получить поля:
{{$json.id}}
{{$json.title}}
{{$json.body}}
Expression - выражение, которое подставляет данные текущего выполнения workflow.
Что общего у Python и n8n
Python:
httpx.get("https://jsonplaceholder.typicode.com/posts/1")
n8n:
Method: GET
URL: https://jsonplaceholder.typicode.com/posts/1
На уровне HTTP оба варианта выполняют одну операцию:
GET /posts/1
Инструмент отличается, но запрос остаётся тем же.
Как передавать query-параметры
Получим публикации пользователя с идентификатором 1:
GET https://jsonplaceholder.typicode.com/posts?userId=1
userId=1 - query-параметр.
Query-параметры в Python
import httpx
url = "https://jsonplaceholder.typicode.com/posts"
params = {
"userId": 1,
}
response = httpx.get(
url,
params=params,
timeout=10.0,
)
response.raise_for_status()
posts = response.json()
print(len(posts))
HTTPX сам добавит параметры к адресу и выполнит кодирование специальных символов.
Несколько параметров:
params = {
"status": "open",
"limit": 20,
"sort": "created_at",
}
Итоговый адрес может выглядеть так:
/tasks?status=open&limit=20&sort=created_at
Query-параметры в n8n
В HTTP Request включите Send Query Parameters и добавьте:
Name: userId
Value: 1
Записывать параметр прямо в URL тоже можно:
https://jsonplaceholder.typicode.com/posts?userId=1
Но отдельные поля удобнее, когда значения приходят из предыдущих узлов.
Например:
Name: userId
Value: {{$json.user_id}}
Почему параметры лучше передавать отдельно
Такой подход:
- сохраняет URL читаемым;
- уменьшает риск ошибки с
?и&; - позволяет подставлять динамические значения;
- корректно кодирует пробелы и специальные символы;
- упрощает добавление и удаление параметров.
Названия параметров не универсальны. Один API использует page, другой offset, третий cursor. Их нужно брать из документации.
Заголовки и авторизация
Рассмотрим запрос к защищённому API:
GET https://api.example.com/profile
Authorization: Bearer TOKEN
Accept: application/json
Заголовки в Python
import httpx
headers = {
"Authorization": "Bearer TOKEN",
"Accept": "application/json",
}
response = httpx.get(
"https://api.example.com/profile",
headers=headers,
timeout=10.0,
)
response.raise_for_status()
profile = response.json()
Значение TOKEN нельзя хранить прямо в опубликованном коде.
Для локального проекта токен можно передать через переменную окружения:
import os
import httpx
token = os.environ["API_TOKEN"]
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json",
}
response = httpx.get(
"https://api.example.com/profile",
headers=headers,
timeout=10.0,
)
Переменная окружения - значение, которое хранится вне исходного кода и передаётся программе при запуске.
Заголовки в n8n
В HTTP Request можно включить Send Headers и добавить заголовки вручную.
Для секретов лучше использовать Credentials - отдельное хранилище n8n для токенов, паролей и других данных доступа.
В зависимости от API можно выбрать:
- Bearer Auth;
- Header Auth;
- Basic Auth;
- OAuth2;
- готовый тип credentials конкретного сервиса.
Если готовый узел n8n не поддерживает нужную операцию, его credentials иногда можно использовать в HTTP Request через Predefined Credential Type.
Что нельзя публиковать
Не размещайте в статье, репозитории или скриншоте:
- API-ключ;
- access token;
- refresh token;
- пароль;
- секрет OAuth;
- приватный сертификат;
- экспорт workflow с открытыми секретами.
Если ключ оказался в открытом доступе, его нужно отозвать и создать новый.
Как отправить POST-запрос с JSON
Создадим публикацию в тестовом API:
POST https://jsonplaceholder.typicode.com/posts
Content-Type: application/json
{
"title": "Подключение внешнего API",
"body": "Запрос отправлен из программы",
"userId": 1
}
Используется POST, потому что запрос создаёт новый объект.
POST-запрос в Python
import httpx
url = "https://jsonplaceholder.typicode.com/posts"
payload = {
"title": "Подключение внешнего API",
"body": "Запрос отправлен из Python",
"userId": 1,
}
response = httpx.post(
url,
json=payload,
timeout=10.0,
)
response.raise_for_status()
created_post = response.json()
print(created_post)
Параметр json=payload выполняет две задачи:
Преобразует словарь Python в JSON
-
Добавляет подходящий Content-Type
Не нужно вручную превращать словарь в строку, если библиотека предоставляет параметр json.
Сервер вернёт примерно такой ответ:
{
"title": "Подключение внешнего API",
"body": "Запрос отправлен из Python",
"userId": 1,
"id": 101
}
JSONPlaceholder имитирует создание объекта, но не сохраняет его как настоящая рабочая база.
POST-запрос в n8n
Настройки:
Method: POST
URL: https://jsonplaceholder.typicode.com/posts
Send Body: включено
Body Content Type: JSON
Тело:
{
"title": "Подключение внешнего API",
"body": "Запрос отправлен из n8n",
"userId": 1
}
Если данные приходят из предыдущего узла:
{
"article_title": "HTTP и API",
"article_text": "Черновик публикации",
"author_id": 3
}
их можно подставить через expressions:
{{
{
title: $json.article_title,
body: $json.article_text,
userId: $json.author_id
}
}}
Так сохраняются исходные типы данных. Если author_id является числом, он не превратится в строку.
Как читать HTTP-ответ
HTTP-ответ состоит не только из JSON.
Полезно проверять:
Статус-код
Заголовки
Тело
Итоговый URL
Ответ в Python
print(response.status_code)
print(response.headers)
print(response.text)
Если ответ содержит JSON:
data = response.json()
Если сервер возвращает файл:
file_bytes = response.content
Если нужен обычный текст:
text = response.text
Не следует вызывать response.json() автоматически для любого ответа. Сервер может вернуть HTML, текст, файл или пустое тело.
Ответ в n8n
Результат HTTP Request становится входом следующего узла.
Если сервер вернул:
{
"id": 481,
"status": "created"
}
можно использовать:
{{$json.id}}
{{$json.status}}
Некоторые API возвращают массив:
[
{
"id": 1,
"title": "Первая публикация"
},
{
"id": 2,
"title": "Вторая публикация"
}
]
В n8n такие записи могут стать отдельными items, после чего следующие узлы выполнятся для каждого элемента.
Item - отдельная единица данных, проходящая через workflow.
Обработка ошибок в Python
Нужно различать две ситуации:
Запрос не удалось отправить или получить ответ
-
Сервер ответил, но вернул ошибочный статус
Пример:
import httpx
url = "https://api.example.com/data"
try:
response = httpx.get(url, timeout=10.0)
response.raise_for_status()
data = response.json()
except httpx.TimeoutException:
print("Сервис не ответил вовремя")
except httpx.HTTPStatusError as error:
print(
"API вернул ошибку:",
error.response.status_code,
error.response.text,
)
except httpx.RequestError as error:
print(
"Не удалось выполнить запрос:",
str(error),
)
TimeoutException означает, что операция превысила время ожидания.
HTTPStatusError возникает после raise_for_status(), когда сервер вернул ошибочный статус.
RequestError охватывает сетевые проблемы, например ошибку соединения или DNS.
В рабочем приложении ошибку обычно не только печатают. Её записывают в журнал, связывают с идентификатором операции и передают в систему наблюдения.
Основные ошибки API
400 Bad Request
Сервер считает запрос некорректным.
Причины:
- повреждён JSON;
- отсутствует обязательное поле;
- дата записана в неправильном формате;
- параметр передан не в той части запроса;
- строка отправлена вместо числа.
Повтор того же запроса без исправления обычно не поможет.
401 Unauthorized
Сервер не смог подтвердить доступ.
Проверьте:
- передаётся ли токен;
- не истёк ли он;
- правильный ли заголовок;
- нет ли лишнего пробела;
- используется ли нужная учётная запись.
403 Forbidden
Сервер распознал клиента, но запретил операцию.
Например, токен разрешает чтение, но не создание или удаление объектов.
404 Not Found
Сервер не нашёл endpoint или ресурс.
Причины:
- опечатка в URL;
- неправильная версия API;
- неверный идентификатор;
- удалённый объект.
405 Method Not Allowed
Адрес существует, но не поддерживает выбранный метод.
Например:
GET /tasks - разрешён
POST /tasks - разрешён
DELETE /tasks - не разрешён
415 Unsupported Media Type
Сервер не поддерживает формат тела.
Например, API ожидает JSON, а программа отправляет обычный текст или данные формы.
422 Unprocessable Content
Структура запроса понятна, но значения не проходят проверку:
{
"email": "не адрес",
"price": -500
}
429 Too Many Requests
Превышено допустимое число обращений.
Сервер может вернуть Retry-After - указание, через какое время стоит повторить запрос.
500, 502, 503 и 504
Эти ответы обычно связаны с сервером или промежуточной инфраструктурой:
500 - внутренняя ошибка приложения
502 - шлюз получил некорректный ответ
503 - сервис временно недоступен
504 - шлюз не дождался ответа
Повтор иногда помогает, но изменяющие запросы нельзя повторять без проверки результата.
Например, POST /orders мог создать заказ, а клиент получил 504 только из-за задержки ответа. Новый POST способен создать второй заказ.
Как диагностировать неработающий запрос
Не стоит одновременно менять метод, URL, заголовки и тело. Так невозможно понять, что именно исправило или сломало запрос.
Используйте последовательную проверку.
1. Упростите запрос
Уберите динамические значения и подставьте известные данные.
Вместо:
https://api.example.com/users/{{$json.user_id}}
проверьте:
https://api.example.com/users/42
В Python временно замените переменную фиксированным значением.
Если статический запрос работает, проблема находится во входных данных или их преобразовании.
2. Проверьте метод
Сравните выбранный метод с документацией.
Одинаковый адрес с GET и POST может выполнять разные операции.
3. Проверьте полный URL
Убедитесь, что правильно указаны:
https;- домен;
- версия API;
- путь;
- идентификатор;
- query-параметры.
4. Проверьте авторизацию
Сравните не только токен, но и способ его передачи:
Authorization: Bearer TOKEN
X-API-Key: TOKEN
Query-параметр api_key
Это разные варианты.
5. Проверьте заголовки
Особенно:
Authorization
Content-Type
Accept
X-API-Key
6. Проверьте тело
Сравните с документацией:
- названия полей;
- вложенность;
- обязательные значения;
- типы данных;
- формат дат;
- допустимые варианты.
7. Изучите тело ошибки
Статус 422 сообщает только класс проблемы. Тело может указать конкретное поле:
{
"error": "validation_failed",
"field": "email",
"message": "Invalid email format"
}
8. Сравните с рабочим примером
Если запрос работает в Postman, curl или примере документации, сравните:
Метод
URL
Параметры
Заголовки
Авторизацию
Тело
Типы значений
Запросы, которые выглядят похожими, часто отличаются одним заголовком или расположением параметра.
9. Возвращайте динамику постепенно
После успешного статического запроса заменяйте значения переменными или expressions по одному.
Готовая интеграция, SDK или прямой HTTP-запрос
Подключаться к API можно несколькими способами.
Готовая интеграция
Например, узел Telegram в n8n.
Преимущества:
- меньше ручной настройки;
- понятные поля;
- встроенная авторизация;
- ниже вероятность ошибиться в структуре запроса.
Ограничение - готовый узел может не поддерживать новую или редкую операцию API.
SDK
SDK - библиотека, которую разработчики сервиса подготовили для конкретного языка.
Пример условного SDK:
client.tasks.create(
title="Позвонить клиенту",
priority="high",
)
Внутри библиотека всё равно обращается к API, но скрывает ручную сборку запроса.
SDK удобен, если он:
- официальный или хорошо поддерживается;
- совместим с текущей версией API;
- поддерживает нужные операции;
- не усложняет обработку ошибок.
Прямой HTTP-запрос
Используется, когда:
- готовой интеграции нет;
- SDK отсутствует;
- нужная операция не поддерживается;
- требуется новый endpoint;
- нужна нестандартная структура запроса;
- необходимо полностью контролировать параметры и тело.
Логика выбора:
Есть подходящая готовая интеграция или SDK - Используем её
Нужной возможности нет - Обращаемся к API напрямую
Прямой HTTP-запрос не является запасным или неправильным способом. Это базовый механизм, на котором строятся готовые интеграции.
Сквозной пример: заявка передаётся во внешнюю CRM
Пользователь отправляет форму:
{
"name": "Анна",
"email": "anna@example.com",
"message": "Нужна автоматизация обработки заказов"
}
Программа должна создать заявку во внешней CRM.
HTTP-запрос:
POST https://api.example.com/leads
Authorization: Bearer TOKEN
Content-Type: application/json
{
"name": "Анна",
"email": "anna@example.com",
"comment": "Нужна автоматизация обработки заказов",
"source": "website"
}
Ответ:
{
"id": 8412,
"status": "created",
"manager": "Иван Петров"
}
Реализация в Python
import os
import httpx
token = os.environ["CRM_API_TOKEN"]
lead = {
"name": "Анна",
"email": "anna@example.com",
"comment": "Нужна автоматизация обработки заказов",
"source": "website",
}
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json",
}
try:
response = httpx.post(
"https://api.example.com/leads",
headers=headers,
json=lead,
timeout=10.0,
)
response.raise_for_status()
created_lead = response.json()
print("Создана заявка:", created_lead["id"])
except httpx.HTTPError as error:
print("Не удалось создать заявку:", error)
Реализация в n8n
Workflow:
Webhook
-
Проверка полей
-
HTTP Request
-
Уведомление менеджеру
Настройки HTTP Request:
Method: POST
URL: https://api.example.com/leads
Authentication: Bearer Auth
Body Content Type: JSON
Тело:
{{
{
name: $json.name,
email: $json.email,
comment: $json.message,
source: "website"
}
}}
После ответа CRM следующий узел может использовать:
{{$json.id}}
{{$json.manager}}
Например:
Заявка {{$json.id}} создана и назначена менеджеру {{$json.manager}}.
На уровне HTTP Python и n8n отправляют одну и ту же операцию. Различается только способ её настройки.
Итог
Чтобы подключить внешний API к программе:
1. Найдите нужную операцию в документации.
2. Определите endpoint и HTTP-метод.
3. Узнайте способ авторизации.
4. Передайте query-параметры в URL.
5. Добавьте требуемые заголовки.
6. Сформируйте тело в указанном формате.
7. Отправьте сначала простой статический запрос.
8. Проверьте статус-код и тело ответа.
9. Добавьте динамические данные.
10. Настройте обработку ошибок и ограниченные повторы.
Python, n8n, Postman и curl не создают разные виды API. Они по-разному помогают сформировать один и тот же HTTP-запрос.
Главный навык - не запомнить расположение полей в конкретном инструменте, а научиться читать документацию и переносить её требования в метод, URL, параметры, заголовки и тело запроса.