Python POST Requests

In the last lesson you learned GET Requests. There you asked a server for data and read it back. So GET is for reading. But what about the other direction? Sometimes you need to send something to a server. Think of signing up for a new account. Or posting a comment on YouTube. Or submitting a form. That is what a POST request is for.

🤔 Why POST Requests?

Here is the pain. A GET request only reads data. You cannot use it to create a new user, save a comment, or submit an order. GET asks “give me this”. It does not say “here, take this and store it”.

POST fixes that. A POST request carries a little package of data along with it. The server takes that data and usually creates or saves something new. So:

  • GET means read something that already exists.
  • POST means send something new for the server to store.

It is like the difference between reading a letter someone sent you and writing and mailing a letter yourself. GET reads. POST sends.

📦 What Goes Inside a POST Request

A POST request is built from a couple of pieces.

  • The URL, which is the address you are sending to.
  • The body, which is the actual data you are sending. This is usually in JSON format.

JSON is just a way to write data as key and value pairs. It looks a lot like a Python dictionary. So {"name": "Alex", "job": "developer"} is a tiny package of data with two fields in it.

Note

We will use a free public test API called https://jsonplaceholder.typicode.com. It is made for practice. It pretends to create your data and sends back a realistic response, but nothing is really saved. Perfect for learning without breaking anything.

🧩 Your First POST Request

The requests library is third party, so install it first if you have not already.

Terminal window
pip install requests

Now let us send a small piece of data to the server and read back what it says.

import requests
# The data we want to send, as a Python dictionary
new_post = {
"title": "My first post",
"body": "Learning POST requests is fun!",
"userId": 1
}
# Send the data with a POST request
response = requests.post(
"https://jsonplaceholder.typicode.com/posts",
json=new_post
)
print("Status code:", response.status_code)
print("Server replied with:")
print(response.json())

Let us walk through it line by line.

  • new_post is a normal Python dictionary. This is the data we want to create on the server.
  • requests.post(...) sends that data. The first argument is the URL. The second is json=new_post, which packs our dictionary into JSON and puts it in the body of the request.
  • response.status_code is a number the server sends back to tell us how it went.
  • response.json() turns the server’s reply back into a Python dictionary so we can read it.

Here is the kind of reply you get back.

Output

Status code: 201
Server replied with:
{'title': 'My first post', 'body': 'Learning POST requests is fun!', 'userId': 1, 'id': 101}

This is example output. The exact reply depends on the live server, so yours may look slightly different. But the shape will be the same.

Notice what changed here. The status code is 201, not 200. And the server added an id field of 101 that we did not send.

🔢 What Does 201 Mean?

When a GET request works, the server usually replies with 200. That means “OK, here is your data”. But when a POST request creates something new, the server replies with 201. That means “Created”. It is the server’s way of saying “Got it, I made a new record for you”.

That new id of 101 in the reply is the server telling you the ID it gave to the thing you just created. Real servers do this so you know how to find your record again later.

Here are the status codes you will see most often.

Code Meaning When you see it
200 OK A GET request read data fine
201 Created A POST request made a new record
400 Bad Request You sent data the server did not like
404 Not Found The URL does not exist

🆚 GET vs POST Side by Side

It helps to see both next to each other. Same library, same style, but they do opposite jobs.

import requests
# GET: read a post that already exists
read = requests.get("https://jsonplaceholder.typicode.com/posts/1")
print("GET status:", read.status_code)
print("Title we read:", read.json()["title"])
# POST: send a new post to create
sent = requests.post(
"https://jsonplaceholder.typicode.com/posts",
json={"title": "Hello", "body": "Some text", "userId": 2}
)
print("POST status:", sent.status_code)
print("ID we got:", sent.json()["id"])

The GET call reads post number 1 and pulls its title out. The POST call sends a brand new post and reads back the ID the server assigned.

Output

GET status: 200
Title we read: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
POST status: 201
ID we got: 101

This is example output from the live test API. The GET title comes from the server’s sample data. The POST id is the placeholder id the test API hands back.

🙋 A More Real Example

Imagine Riya is building a sign-up form. When someone fills it in and clicks the button, your code needs to send their details to the server to create their account. That is a POST request.

This snippet sends a new user’s details and checks whether it worked.

import requests
def sign_up(name, email):
new_user = {
"name": name,
"email": email
}
response = requests.post(
"https://jsonplaceholder.typicode.com/users",
json=new_user
)
if response.status_code == 201:
data = response.json()
print(f"Account created for {data['name']}!")
print(f"Your new user ID is {data['id']}.")
else:
print("Something went wrong. Status:", response.status_code)
sign_up("Arjun", "arjun@example.com")

Walking through it:

  • sign_up takes a name and an email, then puts them in a dictionary called new_user.
  • It POSTs that dictionary to the /users address.
  • It checks the status code. If it is 201, the account was created, so we read the reply and print a friendly message. If not, we tell the user something went wrong.

Output

Account created for Arjun!
Your new user ID is 11.

This is representative output. A real sign-up server would save the user for real and give back a real ID. The test API just pretends, but your code would work exactly the same against a real one.

⚠️ Common Mistakes

A few slip-ups catch almost everyone the first time.

  • Using data= when you mean json=. The json= argument packs your dictionary as JSON and sets the right header for you. Use it when sending JSON.
# ❌ Avoid: sends as a plain form, not JSON
requests.post(url, data={"title": "Hi"})
# ✅ Good: sends proper JSON with the right header
requests.post(url, json={"title": "Hi"})
  • Forgetting to check the status code. If you assume it worked and just read the reply, your program will behave oddly when it actually failed.
# ❌ Avoid: blindly trusting it worked
result = response.json()
# ✅ Good: confirm it was created first
if response.status_code == 201:
result = response.json()
  • Sending a Python object that is not JSON friendly. JSON only understands simple things like strings, numbers, lists, True, False, and dictionaries. A datetime or a custom class will fail. Convert it to a string or number first.

✅ Best Practices

Small habits that keep your POST code safe and easy to read.

  • Always check response.status_code before you trust the reply. A 201 means it worked.
  • Use json= for JSON data so requests sets the headers for you. Do not build the JSON string by hand.
  • Keep the data you send in a clearly named dictionary. It reads better than stuffing a big dictionary inline.
  • Wrap real network calls in a try/except later on, because the internet can drop and servers can be slow. We will build on this idea soon.

🧩 What You’ve Learned

A quick recap of what you can now do.

  • ✅ You know POST sends new data while GET only reads existing data.
  • ✅ You can send a dictionary with requests.post(url, json=your_dict).
  • ✅ You can read the server’s reply with response.json().
  • ✅ You know a 201 status code means a new record was created.
  • ✅ You can spot the new id the server assigns to what you created.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What is the main difference between a GET request and a POST request?

    Why: GET is for reading data that already exists, while POST sends a package of data for the server to create or store.

  2. 2

    Which argument sends a Python dictionary as JSON in a POST request?

    Why: Passing json=your_dict packs the dictionary into JSON and sets the correct header automatically.

  3. 3

    What status code usually means a POST request successfully created a new record?

    Why: A 201 status code means 'Created', telling you the server made a new record from your data.

  4. 4

    After a successful POST, what useful extra field does the server often add to its reply?

    Why: Servers usually add an id field so you know the identifier assigned to the thing you just created.

🚀 What’s Next?

You can now read data with GET and send data with POST. But many real servers will not let you in until you prove who you are. Next you will learn how to do exactly that.

API Authentication

Share & Connect