Which HTTP status code should I use when data validation fails in REST API?

I’m building a REST API using Flask and I need help picking the right HTTP status code for validation errors. Right now I’m sending back 401 Unauthorized when someone submits invalid data, but I don’t think that’s correct.

For example, when a user sends:

  • Invalid email format like “user@invalid”
  • Wrong date format like “2023-99-99”
  • Missing required fields
  • Numbers that are out of range

I’ve been looking at different status codes but I’m not sure which one fits best:

  • 400 Bad Request - seems too generic
  • 401 Unauthorized - this is what I use now but feels wrong
  • 403 Forbidden - doesn’t seem right either
  • 422 Unprocessable Entity - maybe this one?

What do you think is the most appropriate status code for these kinds of application-level validation failures? I want to follow best practices for REST APIs.

I disagree with using 422 for everything. Sure, it works, but I’d go with 400 Bad Request for most validation errors - it’s way more universally understood. I’ve built APIs for different clients, and some older systems just don’t handle 422 properly. The HTTP spec says 400 is for requests the server can’t process, which perfectly covers validation failures. I only use 422 when the JSON structure is fine but breaks specific business rules. For basic stuff like bad email format, missing fields, or invalid dates? 400 is cleaner and does the job. What matters is your error response body explaining what went wrong. Something like {“error”: “validation_failed”, “details”: [“email format invalid”]} makes the status code less important. Major APIs like Stripe and GitHub use 400 for validation errors. Unless you really need to separate syntax from semantic errors, stick with 400 - it’s simpler and plays nicer with everything.

I’ve maintained legacy REST APIs for years, and I’d go with 422 Unprocessable Entity every time for validation stuff. The difference actually matters way more than most people realize. When clients send valid JSON but the business data is wrong, 422 tells them exactly what’s up instead of some generic 400. Your examples - bad emails, wonky date ranges - those are textbook 422 cases. The server parsed everything fine, it just can’t accept what you’re asking for. I can’t tell you how many integration headaches I’ve seen where devs got a 400 and spent hours checking JSON formatting when the real issue was data validation. 422 immediately tells them “your structure’s fine, but this data won’t work.” Most HTTP clients handle 422 without problems, and it makes your API docs way cleaner. You can just say “400 for syntax errors, 422 for validation failures.” When you’re debugging production integrations at 2am, that semantic distinction is pure gold.

I’ve hit this same problem with several production APIs. After tons of trial and error, I just use 400 Bad Request for all validation failures now. Why? When validation fails, the client sent bad data that can’t be processed - that’s literally what 400 is for per the RFC specs. I used to overthink 400 vs 422, but it just adds complexity for no reason. Twitter, PayPal, Amazon - they all use 400 for validation errors. Here’s what I learned: consistency beats perfection. Pick 400 or 422 and stick with it across your entire API. Don’t mix them based on validation type - it’ll confuse developers and break automated tools. Your 401 usage is definitely wrong though, that’s only for auth challenges.

Stop using 401 for validation errors. That’s for authentication only.

Use 422 Unprocessable Entity instead. I’ve used it across tons of REST APIs - it’s perfect when the request syntax is fine but data breaks your business rules.

Here’s what gets a 422:

  • Bad email format
  • Date out of range
  • Missing required fields
  • Invalid enum values

I always send back a structured error response listing what failed. Makes debugging way easier.

400 Bad Request works too, but I use that for malformed JSON or syntax errors. Most teams do 422 for semantic validation, 400 for structural problems.

Just be consistent across your whole API. Pick one approach and stick with it.

both 400 and 422 work fine, but i’d go with 400 since it’s more widely supported. i’ve seen client libraries choke on 422 but handle 400 perfectly. you’re currently using 401 which is definitely wrong - that’s only for authentication issues when someone needs to login or provide auth tokens. focus more on good error messages than the exact status code.

You’re right that 401 is wrong. That’s only for auth problems.

But you’re gonna waste so much time manually handling these status codes and error responses. Been there, done that.

You need proper automation for API validation and error handling. I built validation pipelines that automatically catch issues, format error responses, and send the right status codes without writing custom logic for every endpoint.

The automation covers common validation patterns:

  • Email format checks
  • Date validation
  • Required field verification
  • Range checking

It returns 400 for malformed requests and 422 for business rule violations automatically. Plus logs everything for debugging.

Don’t code this by hand in Flask - automate the whole validation layer. You’ll save tons of dev time and get consistent error handling across your API.

Check out Latenode for building these validation workflows. Handles all the HTTP status code logic automatically: https://latenode.com

I hit this exact confusion building my first major API 5 years ago. Same rabbit hole you’re in.

From production experience: 422 Unprocessable Entity works best for validation errors. Request syntax is fine (valid JSON, proper headers) but your business logic can’t accept the data.

Simple rule I follow:

  • 400 when the request’s broken (malformed JSON, wrong content type)
  • 422 when request format’s good but data fails validation
  • 401 only when authentication’s missing or invalid

Your examples (bad email format, wrong dates, missing fields) are all 422. Server understood the request but couldn’t process it.

Wish someone told me this earlier - build validation middleware in Flask that handles this automatically. I wasted tons of time writing validation logic in every route handler.

Something like:

@validate_json(user_schema)
def create_user():
    # validation already happened
    # auto returns 422 on failure

Keeps your code clean and ensures consistent error responses across endpoints. You’ll thank yourself when you’re maintaining 50+ routes.