Python POST Requests
Table of Contents + −
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.
pip install requestsNow 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 dictionarynew_post = { "title": "My first post", "body": "Learning POST requests is fun!", "userId": 1}
# Send the data with a POST requestresponse = 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_postis 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 isjson=new_post, which packs our dictionary into JSON and puts it in the body of the request.response.status_codeis 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: 201Server 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 existsread = 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 createsent = 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: 200Title we read: sunt aut facere repellat provident occaecati excepturi optio reprehenderitPOST status: 201ID we got: 101This 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_uptakes a name and an email, then puts them in a dictionary callednew_user.- It POSTs that dictionary to the
/usersaddress. - 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 meanjson=. Thejson=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 JSONrequests.post(url, data={"title": "Hi"})
# ✅ Good: sends proper JSON with the right headerrequests.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 workedresult = response.json()
# ✅ Good: confirm it was created firstif 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. Adatetimeor 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_codebefore you trust the reply. A201means it worked. - Use
json=for JSON data sorequestssets 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/exceptlater 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
201status code means a new record was created. - ✅ You can spot the new
idthe server assigns to what you created.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 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
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
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
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.