Enviar um POST JSON com Python requests

requests.post(url, json=payload) envia um corpo JSON a um servidor em uma única linha. O argumento json= serializa o dicionário, define o cabeçalho Content-Type: application/json e escreve o corpo da requisição. A resposta retorna um código de status e, na maioria das APIs, os dados que o servidor armazenou, permitindo confirmar que a chamada fez o que era esperado. Este é o padrão por trás de escritas em REST APIs, webhooks e qualquer endpoint que espera uma carga JSON.

Exemplo Python requests.post para JSON

Saída:

A saída aparecerá aqui...

Saída:

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

Como Este Exemplo Funciona

  1. json= serializa o dict e define Content-Type: application/json, para que o servidor receba um objeto JSON em vez de campos de formulário.
  2. timeout=10 limita a espera, para que um endpoint inacessível falhe rapidamente em vez de bloquear a execução.
  3. raise_for_status() converte uma resposta 4xx ou 5xx em exceção, para que uma resposta com falha nunca flua silenciosamente para a próxima linha.
  4. response.json() retorna a resposta como um dict Python. Esta API de teste devolve seus campos e adiciona um id atribuído pelo servidor; o status 201 Created confirma que a chamada foi tratada como um novo recurso.

Verificar o que o Servidor Realmente Recebeu

Dois sinais independentes confirmam que o POST funcionou; uma verificação confiável usa os dois. O corpo da resposta prova que o conteúdo chegou: campos devolvidos mais um novo id significam que o servidor interpretou o JSON. Run Details prova o transporte: registra a requisição que o navegador realmente enviou.

Sinal do Run DetailsO que confirma
Um POST para jsonplaceholder.typicode.com, status 201A chamada saiu como POST e o servidor criou um recurso
A coluna de método mostra POST, não GETrequests.post enviou o verbo desejado
A duração da requisição em msTempo de ida e volta, medido separadamente do parsing JSON
Status bloqueado ou falhou, sem código HTTPA requisição não chegou ao servidor (ver CORS abaixo)
Packages lista requestsweb.run carregou o pacote requests ao importar

Quando o código de status é 2xx mas o corpo retornado não tem seus campos, o transporte funcionou e o conteúdo não. Essa distinção é o que a maioria dos tutoriais de POST não consegue mostrar, pois nunca colocam a requisição enviada e a resposta do servidor lado a lado.

json vs data: O Bug Silencioso de Perda de Dados

Enviar o mesmo payload com data= em vez de json= faz o servidor responder com 2xx mesmo assim, mas o corpo retornado não terá seus campos. A requisição estava correta; a codificação estava errada.

ArgumentoO que requests enviaO que o servidor interpreta
json={"stars": 3}corpo JSON e Content-Type: application/jsonum objeto JSON
data={"stars": 3}corpo form-encoded e application/x-www-form-urlencodedcampos de formulário, não JSON
data=json.dumps({"stars": 3})string JSON bruta sem cabeçalho JSONtexto que pode se recusar a interpretar como JSON

Regra: passe um dict para json= em qualquer API que espere um corpo JSON, e use data= apenas ao enviar um formulário HTML. Uma API JSON que recebe form encoding normalmente responde 2xx e descarta os dados silenciosamente, por isso verificar apenas o código de status nunca é suficiente.

Quando um POST do Navegador É Bloqueado, Não Rejeitado

Um POST cross-origin se comporta diferente no navegador do que em um script de servidor. Como o corpo é application/json, o navegador envia um preflight CORS OPTIONS antes do POST. Se o endpoint não retornar os cabeçalhos Access-Control-Allow-Origin e Access-Control-Allow-Headers correspondentes, o navegador bloqueia o POST antes que ele saia.

Quando isso acontece, requests lança um erro de conexão como requests.exceptions.ConnectionError, não HTTPError, e Run Details marca a requisição como bloqueada ou falhou sem código HTTP. Um 403 ou 422 é o sinal contrário: a requisição chegou ao servidor, que decidiu rejeitá-la.

Regra: nenhum código HTTP significa um problema de rede ou CORS antes do servidor; um código HTTP significa que o servidor respondeu, mesmo que com erro. jsonplaceholder é compatível com CORS, então o exemplo envia corretamente. Muitas APIs públicas não são, razão pela qual o mesmo código requests.post pode funcionar em um terminal mas falhar neste runtime ou em qualquer Python no navegador.

Erros Comuns

Erro: enviar um dict com data= quando a API espera JSON.

Incorreto:

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

Correto:

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

Por que acontece: data= form-codifica o dict e define um Content-Type de formulário, então uma API JSON não recebe objeto JSON e seus campos desaparecem do resultado.

Erro: serializar o corpo duas vezes.

Incorreto:

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

Correto:

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

Por que acontece: json= já serializa o valor. Passar uma string já construída a codifica duplamente como string JSON em vez de um objeto.

Enviar Cabeçalhos e Auth com o POST

A maioria das APIs de escrita requer autenticação, que viaja nos cabeçalhos da requisição ao lado do corpo:

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

requests combina seus cabeçalhos com o Content-Type que define para json=, então você fornece apenas o que é específico da API. Nunca codifique um token real em código compartilhado; leia-o de uma variável de ambiente. Um cabeçalho personalizado como Authorization já é suficiente para acionar o preflight CORS descrito acima. Para o lado de leitura correspondente, veja ler JSON de uma URL, e envolva a chamada em try/except para tratar erros de conexão.

FAQ

Como enviar um POST em Python?

Chame requests.post(url, json=payload) com um dicionário como carga. O argumento json= serializa o dict, define o cabeçalho Content-Type: application/json e envia em uma única chamada. Leia a resposta com response.json().

Por que meu POST retorna 200 mas o servidor ignora os dados?

O corpo foi enviado no formato errado, normalmente como campos de formulário via data= em vez de JSON via json=. O servidor responde 2xx à requisição em si mas nunca interpreta um objeto JSON. Mude para json= e confirme que o servidor devolveu seus campos.

Por que meu POST com requests está bloqueado no navegador?

Um POST JSON cross-origin aciona um preflight CORS. Se o endpoint não retornar Access-Control-Allow-Origin e Access-Control-Allow-Headers, o navegador bloqueia a requisição antes que ela saia; requests lança um erro de conexão sem código HTTP em vez de um 4xx. O mesmo código pode funcionar em um terminal.

Qual é a diferença entre json e data no requests.post?

json= serializa um dict em corpo JSON e define Content-Type: application/json. data= envia campos form-encoded ou uma string bruta sem o cabeçalho JSON. Use json= para APIs JSON e data= para envios de formulários HTML.