Отправка JSON POST-запроса с Python requests

requests.post(url, json=payload) отправляет JSON-тело на сервер в одну строку. Аргумент json= сериализует словарь, устанавливает заголовок Content-Type: application/json и формирует тело запроса. Ответ содержит статус-код и, в большинстве API, данные, которые сохранил сервер, что позволяет убедиться, что вызов выполнил задуманное. Это паттерн для записи в REST API, вебхуков и любых эндпоинтов, ожидающих JSON-нагрузку.

Пример Python requests.post Для JSON

Вывод:

Результат появится здесь...

Вывод:

Status: 201
Server stored: {'language': 'Python', 'stars': 3, 'id': 101}

Как Работает Этот Пример

  1. json= сериализует словарь и устанавливает Content-Type: application/json, поэтому сервер получает JSON-объект, а не поля формы.
  2. timeout=10 ограничивает ожидание, поэтому недоступный эндпоинт быстро завершается с ошибкой, а не зависает.
  3. raise_for_status() преобразует ответ 4xx или 5xx в исключение, чтобы неудачный ответ не прошёл незаметно дальше.
  4. response.json() возвращает ответ как Python dict. Этот тестовый API возвращает ваши поля и добавляет серверный id; статус 201 Created подтверждает, что вызов был обработан как новый ресурс.

Проверка Того, Что Реально Получил Сервер

Два независимых сигнала подтверждают, что POST сработал; надёжная проверка использует оба. Тело ответа доказывает доставку содержимого: возвращённые поля плюс новый id означают, что сервер разобрал ваш JSON. Run Details доказывает транспорт: он фиксирует запрос, который браузер реально отправил.

Сигнал Run DetailsЧто подтверждает
Один POST на jsonplaceholder.typicode.com, статус 201Запрос ушёл как POST и сервер создал ресурс
Колонка метода показывает POST, не GETrequests.post отправил нужный HTTP-метод
Длительность запроса в мсВремя туда-обратно, отдельно от разбора JSON
Статус заблокирован или ошибка, без HTTP-кодаЗапрос не дошёл до сервера (см. CORS ниже)
Packages показывает requestsweb.run загрузил пакет requests при импорте

Когда статус-код 2xx, но в возвращённом теле нет ваших полей — транспорт сработал, а содержимое не дошло. Это расхождение большинство руководств по POST не могут показать, потому что никогда не сопоставляют отправленный запрос и ответ сервера рядом.

json vs data: Скрытая Потеря Данных

Отправьте тот же payload через data= вместо json= — сервер всё равно ответит 2xx, но в возвращённом теле ваших полей не будет. Запрос прошёл; кодировка была неправильной.

АргументЧто отправляет requestsЧто разбирает сервер
json={"stars": 3}JSON-тело и Content-Type: application/jsonJSON-объект
data={"stars": 3}form-encoded тело и application/x-www-form-urlencodedполя формы, не JSON
data=json.dumps({"stars": 3})сырая JSON-строка без JSON-заголовкатекст, который сервер может отказаться разбирать как JSON

Правило: передавайте словарь в json= для любого API, ожидающего JSON-тело, и используйте data= только при отправке HTML-формы. JSON-API, получивший form-кодировку, обычно отвечает 2xx и тихо теряет данные, поэтому проверки статус-кода никогда не достаточно.

Когда POST Блокируется, а не Отклоняется Сервером

Кросс-доменный POST в браузере ведёт себя иначе, чем в серверном скрипте. Поскольку тело — application/json, браузер отправляет CORS preflight OPTIONS перед POST. Если эндпоинт не возвращает Access-Control-Allow-Origin и Access-Control-Allow-Headers, браузер блокирует POST до его отправки.

В этом случае requests выбрасывает ошибку соединения — requests.exceptions.ConnectionError, а не HTTPError — и Run Details отображает запрос как заблокированный или упавший без HTTP-статуса. Статус 403 или 422 — противоположный сигнал: запрос дошёл до сервера, который решил его отклонить.

Правило: отсутствие HTTP-статуса означает сетевую проблему или CORS до сервера; наличие HTTP-статуса означает, что сервер ответил, пусть даже с ошибкой. jsonplaceholder поддерживает CORS, поэтому пример отправляется чисто. Многие публичные API — нет, поэтому одинаковый код requests.post может работать в терминале, но падать в этом рантайме или любом другом Python в браузере.

Частые Ошибки

Ошибка: отправка словаря через data=, когда API ожидает JSON.

Неправильно:

requests.post(url, data={"stars": 3})

Правильно:

requests.post(url, json={"stars": 3})

Почему это происходит: data= form-кодирует словарь и устанавливает form-заголовок Content-Type, поэтому JSON-API не получает JSON-объект и ваши поля пропадают из результата.

Ошибка: двойная сериализация тела.

Неправильно:

requests.post(url, json=json.dumps({"stars": 3}))

Правильно:

requests.post(url, json={"stars": 3})

Почему это происходит: json= уже сериализует значение. Передача готовой строки двойно кодирует её в строку JSON вместо объекта.

Отправка Заголовков и Аутентификации С POST

Большинство write-API требуют аутентификации, которая передаётся в заголовках запроса вместе с телом:

response = requests.post(
    url,
    json=payload,
    headers={"Authorization": "Bearer TOKEN"},
    timeout=10,
)

requests объединяет ваши заголовки с Content-Type, который устанавливает для json=, поэтому вы указываете только специфичные для API. Никогда не хардкодьте реальный токен в общем коде — читайте его из переменной окружения. Обратите внимание: кастомный заголовок вроде Authorization сам по себе достаточен для запуска CORS preflight, описанного выше. Для чтения данных смотрите чтение JSON из URL, и оберните вызов в try/except для обработки ошибок соединения.

FAQ

Как отправить POST-запрос в Python?

Вызовите requests.post(url, json=payload) со словарём в качестве нагрузки. Аргумент json= сериализует словарь, устанавливает заголовок Content-Type: application/json и отправляет всё за один вызов. Читайте ответ через response.json().

Почему POST возвращает 200, но сервер игнорирует данные?

Тело было отправлено в неправильном формате — обычно как поля формы через data= вместо JSON через json=. Сервер отвечает 2xx на сам запрос, но никогда не разбирает JSON-объект. Переключитесь на json= и убедитесь, что сервер вернул ваши поля обратно.

Почему POST из requests блокируется в браузере?

Кросс-доменный JSON POST вызывает CORS preflight. Если эндпоинт не возвращает Access-Control-Allow-Origin и Access-Control-Allow-Headers, браузер блокирует запрос до его отправки, и requests выбрасывает ошибку соединения без HTTP-статуса, а не 4xx. Тот же код в терминале может работать.

В чём разница между json и data в requests.post?

json= сериализует словарь в JSON-тело и устанавливает Content-Type: application/json. data= отправляет form-encoded поля или сырую строку без JSON-заголовка. Используйте json= для JSON API и data= для отправки HTML-форм.