Learning Platform
Урок 08.01 · 18 мин
Начальный
HTTPREST APIStatus codesTLSIdempotency
HTTP: текстовый протокол поверх TCP — полная анатомия Идемпотентность и safety: почему повтор PUT безопасен, а POST — нет

Зачем DE разбираться в HTTP

90% внешних источников данных в работе data-инженера — это

REST API
: GitHub, Stripe, Salesforce, какой-нибудь внутренний микросервис из соседней команды. И когда такой источник падает, тормозит или возвращает мусор — отлаживать это придётся вам, а не библиотеке requests.

Поэтому первый урок этого модуля — не про код. Это про то, как устроен HTTP на уровне, который нужен, чтобы читать логи, понимать, что говорит сервер, и не наступить на классические грабли вроде «а почему оно повторило POST три раза и я списал деньги клиенту трижды».

К концу урока вы должны без подсказок отвечать на вопросы: что значит 429, какие методы можно повторять, что положить в Authorization, и почему verify=False в production — это увольнение.

HTTP запрос-ответ за 30 секунд

HTTP
— это диалог двух сторон. Клиент (ваш Python-скрипт) посылает запрос, сервер отвечает ответом. Каждое сообщение — это набор текстовых строк строго определённого формата плюс опциональное тело.

Минимальный запрос выглядит так:

GET /repos/python/cpython HTTP/1.1
Host: api.github.com
Accept: application/vnd.github+json
User-Agent: my-etl/1.0

Минимальный ответ:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 4321
X-RateLimit-Remaining: 4998

{"id": 1456, "name": "cpython", "stargazers_count": 60000}

Из этих двух блоков складывается вся работа с REST API. Всё остальное — это варианты заголовков, методов, статусов и тел.

Анатомия HTTP-запроса и ответа

Каждое сообщение состоит из стартовой строки, заголовков и опционального тела, разделённых пустой строкой

ClientPython scriptВаш ETL, который вызывает API
RequestGET /repos/python/cpythonМетод + URL + headers + (опц.) body
Serverapi.github.com
Clientparses JSON
Response200 OK + bodyStatus code + headers + (опц.) body
Serverготовит ответ

HTTP/1.1 vs HTTP/2 vs HTTP/3

Краткая awareness, не более.

HTTP/1.1
— это то, что вы видите в примерах выше: текстовый, простой, человекочитаемый. Один TCP-коннект — один запрос за раз. Если нужно сделать 100 параллельных запросов — нужно 100 TCP-коннектов.

HTTP/2
добавил мультиплексирование (много параллельных запросов в одном коннекте) и бинарный формат. Прикладной код этого не замечает — методы, заголовки, статусы остались те же. Просто работает быстрее.

HTTP/3 поверх

QUIC
— то же самое, но поверх UDP. Для DE-задач разница нулевая. Уметь сказать на собеседовании, что HTTP/2 — это про мультиплексирование, — достаточно.

requests работает только по HTTP/1.1. httpx умеет HTTP/2 опционально (если установить с [http2]). Для подавляющего большинства задач разница незаметна.

Методы

Запрос всегда начинается с метода — глагола, описывающего, что мы хотим сделать с ресурсом.

МетодЧто значитТело?Идемпотентен?
GETПрочитать ресурсНетДа
POSTСоздать новый ресурс / выполнить действиеДаНет
PUTЗаменить ресурс целикомДаДа
PATCHЧастично обновить ресурсДаПо договорённости
DELETEУдалить ресурсОбычно нетДа
HEADКак GET, но без тела ответаНетДа
OPTIONSУзнать, что разрешено на этом URLНетДа

Идемпотентность
— главное практическое свойство. Если запрос идемпотентен, его можно безопасно повторить при ошибке сети. Если нет — повтор может создать дубликаты, дважды списать деньги, отправить два уведомления.

GET, PUT, DELETE — идемпотентные по определению протокола. Сервер обязан их так реализовывать. POST — нет. Если для конкретного POST вы хотите идемпотентность, нужен

Idempotency-Key
заголовок — клиент кладёт в него уникальный ID, сервер обещает, что повторный запрос с тем же ID не создаст дубликат.

POST /v1/charges HTTP/1.1
Host: api.stripe.com
Idempotency-Key: 7b3e9f1a-8c4e-4b9a-9f2e-1a2b3c4d5e6f

Запомните: ретрай-логика, которую мы напишем в уроке 04, должна знать, идемпотентен ли запрос. Иначе она вам однажды задвоит данные в проде.

Status codes

Ответ всегда начинается со статус-кода — трёхзначного числа. Первая цифра определяет класс ответа.

2xx — успех.

  • 200 OK — стандартный успех, тело ответа есть.
  • 201 Created — создан новый ресурс. В заголовке Location обычно URL созданного.
  • 202 Accepted — запрос принят, обработка асинхронна. Будет позже.
  • 204 No Content — успех, тело пустое. Часто после DELETE или PUT.

3xx — редирект.

  • 301 Moved Permanently — ресурс переехал навсегда, в Location новый URL.
  • 302 Found — переехал временно.
  • 304 Not Modified — у клиента уже свежая версия (по If-Modified-Since / ETag). Полезно для кэширования.

requests и httpx редиректы обрабатывают сами по умолчанию. Можно отключить через allow_redirects=False.

4xx — клиент ошибся, повторять не нужно.

  • 400 Bad Request — мусор в запросе, мы что-то невалидное послали.
  • 401 Unauthorized — нет токена / токен невалидный.
  • 403 Forbidden — токен валидный, но прав нет.
  • 404 Not Found — ресурса по этому URL не существует.
  • 409 Conflict — состояние ресурса не позволяет операцию.
  • 422 Unprocessable Entity — JSON распарсился, но валидация по бизнес-правилам провалилась.
  • 429 Too Many Requests — превысили
    rate limit
    . Это единственная 4xx, которую обычно стоит повторить (с задержкой).

5xx — сервер сломался, можно повторить.

  • 500 Internal Server Error — что-то упало на стороне сервера.
  • 502 Bad Gateway — прокси/балансировщик не получил ответ от backend.
  • 503 Service Unavailable — сервис перегружен/в дауне.
  • 504 Gateway Timeout — backend не ответил в срок.

Правило для ретраев: повторяем 5xx, 429, network errors. Не повторяем остальные 4xx. Подробно разберём в уроке 04.

WARNING

200 OK ещё не значит, что в теле то, что вы ждали. Некоторые API на бизнес-ошибки тоже возвращают 200 с полем {"error": "..."} в JSON. Не доверяйте только статус-коду — всегда проверяйте структуру тела.

Заголовки, важные для DE

Заголовки — это пары Имя: значение после стартовой строки. Их сотни, но в DE-работе постоянно встречаются с десяток.

В запросе:

  • Authorization: Bearer eyJ... — токен для аутентификации. Самый частый формат — Bearer (OAuth/JWT).
  • Authorization: Basic dXNlcjpwYXNz — basic auth, base64(user:password). Только поверх HTTPS.
  • Accept: application/json — клиент готов принять JSON. Многие API возвращают разный формат в зависимости от Accept.
  • Content-Type: application/json — формат тела запроса. Обязателен при POST с JSON.
  • User-Agent: my-etl/1.0 — кто стучится. Некоторые API (GitHub) требуют User-Agent.
  • If-Modified-Since: <date> / If-None-Match: <etag> — кэширование, чтобы получить 304 вместо повторного скачивания.

В ответе:

  • Content-Type: application/json; charset=utf-8 — что лежит в теле.
  • Content-Length: 12345 — длина тела в байтах.
  • Cache-Control: max-age=3600 — сколько секунд ответ можно кэшировать.
  • ETag: "abc123" — версия ресурса для условного запроса.
  • Retry-After: 30 — сколько секунд подождать перед повтором (приходит с 429 или 503).
  • Link: <...>; rel="next" — pagination (разберём в уроке 03).
  • X-RateLimit-Limit: 5000 / X-RateLimit-Remaining: 4998 — кастомные заголовки rate-limit. Не стандарт, у каждого API свой набор.
# Пример: запрос к GitHub с правильными заголовками
import httpx

response = httpx.get(
    "https://api.github.com/repos/python/cpython",
    headers={
        "Authorization": "Bearer ghp_xxxxxxxxxxxxxxxxxxxx",
        "Accept": "application/vnd.github+json",
        "User-Agent": "my-etl/1.0",
    },
    timeout=10,
)
print(response.status_code)
print(response.headers["x-ratelimit-remaining"])
print(response.json()["stargazers_count"])

Никакого реального кода с requests/httpx пока не пишем — это следующий урок. Здесь важно понять, что заголовки — это не магия, а просто строки, которые клиент посылает рядом с URL.

TLS и HTTPS

http:// — открытый трафик, любой в сети между вами и сервером может прочитать. https:// — то же самое поверх

TLS
: шифрованное соединение + проверка, что сервер тот, за кого себя выдаёт.

Когда вы делаете httpx.get("https://api.github.com/..."), под капотом происходит:

  1. TCP-соединение на 443 порт.
  2. TLS handshake
    : сервер присылает сертификат, клиент проверяет, что он подписан доверенным
    Certificate Authority
    .
  3. Договорились о ключах — дальше всё шифруется.
  4. Поверх этого работает обычный HTTP.

Самая частая ошибка junior’а:

# КАТЕГОРИЧЕСКИ НЕ ДЕЛАТЬ В PRODUCTION
response = httpx.get("https://api.example.com/data", verify=False)
DANGER

verify=False отключает проверку сертификата. Это значит, что любой провайдер/прокси/злоумышленник в середине может перехватить ваш трафик, подсунуть свой сертификат и читать ваши токены/данные открытым текстом. На корпоративных DE-задачах за verify=False в проде увольняют. Если CI кричит про сертификат — настоящая проблема в certifi, корп-прокси или системных trust-store, а не в проверке.

Легитимные случаи verify=False: только локальное тестирование против self-signed сервера, который вы сами подняли. В коде, который пойдёт в git/прод — никогда.

Tools для exploration

Перед тем как писать Python-код, всегда проверьте API руками. Это сэкономит часы отладки.

curl — стандартная утилита, есть везде:

curl -i https://api.github.com/repos/python/cpython \
  -H "Authorization: Bearer ghp_xxxxxxxxxxxxxxxxxxxx" \
  -H "Accept: application/vnd.github+json" \
  -H "User-Agent: my-etl/1.0"

Флаг -i печатает заголовки ответа. Флаг -v показывает весь TLS-handshake и обмен сообщениями — полезно, когда не понятно, что не так.

httpie — то же, но с человеческим интерфейсом и подсветкой:

http GET https://api.github.com/repos/python/cpython \
  Authorization:"Bearer ghp_xxxx" \
  Accept:application/vnd.github+json

Ставится через uv tool install httpie. Для exploration удобнее curl’а в разы.

Postman / Insomnia — GUI-клиенты. Удобно для сложных запросов с многими параметрами, для коллекций запросов, для шаринга с командой. В DE-работе используется реже, чем curl/httpie.

Browser DevTools → вкладка Network. Если API доступен и через UI приложения — открываете в браузере, делаете действие, смотрите, какой запрос ушёл. Часто это самый быстрый способ разобраться в недокументированном API.

Что мы получили

  • HTTP — это текстовый диалог: запрос (метод + URL + headers + body) и ответ (status + headers + body).
  • Методы делятся на идемпотентные (GET, PUT, DELETE) и нет (POST). От этого зависит, можно ли retry’ить.
  • Статусы группируются по первой цифре: 2xx — успех, 3xx — редирект, 4xx — клиент виноват, 5xx — сервер.
  • Retry’им только 5xx, 429 и network errors. Остальные 4xx — повторять бессмысленно.
  • Заголовки Authorization, Accept, Content-Type, User-Agent, Retry-After — то, что встречается на каждом DE-проекте.
  • HTTPS = HTTP + TLS. Никогда не отключайте verify.
  • Перед кодом — пробуйте API в curl/httpie. Сэкономите часы.

В следующем уроке возьмём requests и httpx, увидим Session, timeouts, raise_for_status — и напишем первый production-ready клиент к GitHub API.

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 7