52

Our service is in 5 cities right now. If someone tries to call our service API from any other city, we want to throw this error Service not available in your area.

The question is, what is the appropriate http code would be for this error?

  • 503: Service Unavailable
  • 403: Forbidden

or something else?

21
  • 52
    "If someone tries to call our service API from any other city" note that IP geolocation is very often wrong, if you forbid any user whose IP geolocates to a different city you will very likely be locking out legitimate users. Jul 12, 2018 at 16:54
  • 43
    Am I not allowed to hail a ride for my child, who is in another city and needs to come to the airport in order to visit me? Jul 12, 2018 at 20:01
  • 30
    While not an answer to the question, HTTP 451 (RFC 7725) may be useful to future readers who find this question. It's main purpose is to indicate content is not available due to a legal request, action, or restriction (copyright, court order, etc.)
    – Tyzoid
    Jul 12, 2018 at 20:20
  • 35
    If there is nothing wrong with the network connectivity, authentication, no errors internally in the application, and syntactically valid input there shouldn't be an error message. With "we don't offer our service in this area" you've left technology issues and gone into business issues, so I'm not sure a HTTP error would be appropriate. I think you should give a 200 and (or 302 and redirect to) an appropriate message about "sorry our service isn't available in your area - yet. But we're expanding - Check back in late 2019!" or whatever the marketing dept comes up with.
    – ivanivan
    Jul 12, 2018 at 21:59
  • 7
    It's unclear from the question whether you mean to block all access to the API based on the client computer's location (geo IP?) or if you're asking for the response code for a particular failed API request (e.g. book?address=123_example_st_london). Is the location part of the input, or do you have the client's location some other way?
    – Ben
    Jul 13, 2018 at 5:13

10 Answers 10

102

Any HTTP error code would be inappropriate. There is no error or problem of any sort from an HTTP perspective so it should be something in the 200 range. You politely inform some of your users that they will not be serviced by sending back a document that tells them so. And this all goes well.

The user will not be able to use your application. That is a conscious decision made by your business logic, not a mishap. On the HTTP level everything is honky dory.

Edit

It looks like what we are looking at here is a clash of old school versus new school. When HTTP was designed, there were no web services, there was no SOAP, no JSON, no REST principles. As a protocol above TCP this was already considered (close to) application level and many high level status codes were defined. When the web started to be used for richer, high level services and a common means to transport "envelopes" was required, designers hi-jacked HTTP rather than defining a newer and cleaner protocol, just because HTTP was ubiquitous.

So in a modern web service context, HTTP is indeed little more than a dumb transport layer and most of its codes may be considered not applicable or obsolete. Just picking one because it comes close to your application state and happens to be in that list that once meant something may seem harmless, but I think it would send a wrong message. You do not want HTTP to play that regulating role in a web service context.

37
  • 59
    By this logic, any application error should be returned as 200. And all requests should be POST, even deletions. This approach treats HTTP as an opaque transport protocol - like TCP. This isn't how web service APIs are usually treated - they try to leverage HTTP semantics a a standard language for APIs. Why not return 401 for Unauthorized, rather than returning 200 with a custom, nonstandard error code? Jul 12, 2018 at 19:17
  • 32
    By this logic, no application should ever return any response in the 400 range. This is precisely the kind of condition that the 400 range of responses is for. Also, does your first sentence apply to all future answers, as well as those that were posted before yours? Jul 12, 2018 at 19:59
  • 31
    I have to agree here that this is just a plain 200. The user has asked a question, we understood it, we know the right answer, and we've provided him with the answer (which happens to be "No"). All's well in HTTP land. Jul 13, 2018 at 0:02
  • 8
    Hehe ... quick journey from "this isn't really an error" to "so, you're saying nothing is ever an error?"
    – svidgen
    Jul 13, 2018 at 1:33
  • 29
    This. "You tried to access a URL that doesn't exist" is very different from "Our company doesn't operate in your area". Only one has anything to do with HTTP.
    – CJ Dennis
    Jul 13, 2018 at 5:20
87

5xx errors are server errors - something went wrong on the server. In particular, a 503 indicates that:

the server is currently unable to handle the request due to a temporary overload or scheduled maintenance

4xx errors are client errors - the client is making a request that the server is unable or unwilling to fulfill. In particular, a 403 indicates that

the server understood the request but refuses to authorize it. A server that wishes to make public why the request has been forbidden can describe that reason in the response payload (if any). [..] However, a request might be forbidden for reasons unrelated to the credentials.

I would argue that 503 is clearly incorrect, because this isn't a temporary issue - you don't support requests in that area, period. The argument could be made that you eventually hope to support the area, but the intent of the code is to include a header indicating when the client can try again. "In 6 months" doesn't adhere to the intent.

403 is a better choice because your service simply forbids requests from certain locales.

8
  • 403 is for cases where access is forbidden, and the client can't do anything about it. But in this case the client can (get in the car with your laptop or phone), so 403 is inappropriate.
    – gnasher729
    Jul 12, 2018 at 22:27
  • 2
    @gnasher729 4xx errors are for things the client might be able to fix, that includes 403. The (linked) spec specifically states the client can retry with different credentials (assuming credentials were the issue).
    – jaxad0127
    Jul 12, 2018 at 23:43
  • 23
    @gnasher729 403 does not mean the human cannot do anything about it. It means the browser cannot do anything about it. The human can always do a password reset, talk to the administrator or in this case move to another city.
    – slebetman
    Jul 13, 2018 at 0:45
  • 1
    @gnasher729 The client is not the user. Jul 13, 2018 at 12:39
  • 10
    5xx = Oops our bad, hang on while we fix the problem. 4xx = We're sorry but by policy we're unable to accommodate your request at this time. Here's why.... Jul 13, 2018 at 13:26
47

Neither of those.

If your API is well-designed, the URL includes the name of the city, e.g.

http://example.com/API/Vienna/HailRide

or

http://example.com/API/HailRide?city=Vienna

since IP geolocation is unreliable, your users might be using VPNs, your users might want hail a ride for someone else, etc. Suggesting a city based on the user's location is the API client's responsibility. Usually, the client has much better resources for determining the user's location anyway (for example, a mobile device's location service).

Once you've done that, the correct answer to

http://example.com/API/SomeUnsupportedCity/HailRide

or

http://example.com/API/HailRide?city=SomeUnsupportedCity

becomes obvious: 404 Not Found: No resource for hailing a ride at SomeUnsupportedCity exists.

7
  • 6
    This should be the accepted answer: API design is not only about complying with old numerical codes, and the scenario about vpn's etc is quite realistic.
    – Edoardo
    Jul 13, 2018 at 9:18
  • 10
    I have to disagree with this. Including the name of the city in the URL could lead to all sorts of problems down the road, because it forces the client to determine a city name based on the location, and you well may not like the result. For example, are you in Los Angeles or Beverly Hills? London or Westminster? What happens if the client thinks it's in Richardson, but you want an API to serve the entire Dallas area? It would be a better idea for the client to just supply the pickup/dropoff locations; the server can determine whether they're within the service area and respond accordingly. Jul 13, 2018 at 22:42
  • 11
    @ZachLipton I'm pretty sure the city names were an example only, it depends on the OP's actual business rules how they define "city" or "location". It could as well be a coordinate.
    – Bergi
    Jul 14, 2018 at 8:37
  • 1
    @ZachLipton no, the point here is not having the service use client IP to understand the location, that is just wrong
    – Edoardo
    Jul 15, 2018 at 20:00
  • 3
    @eddyce I agree that using the client IP is a bad idea for many reasons. I'm saying that /API/Vienna/HailRide is also prone to problems, because it requires the client to convert locations into city names, and that's not a 1-to-1 mapping that corresponds with the areas a company does business. Jul 15, 2018 at 20:15
27

This seems like a round hole/square peg question. Why does your only response need to be an HTTP code? HTTP error codes can't possibly cover all use cases.

All of your API calls should have additional messaging that comes back - i.e. a little JSON error message. Give them an 403 (because, truly they don't have the permission to use the API given the location) and return an additional bit of information as you're suggesting.

If you don't do this then next time you'll ask what HTTP error code to return when the user asked for an SUV but only a Prius is available.

3
  • 5
    +1 for your last sentence. Sums it up nicely. Jul 13, 2018 at 1:05
  • 1
    Well that would pretty clearly need to be 3xx redirect of some kind. ;)
    – Brandon
    Jul 13, 2018 at 21:20
  • The last case probably warrants a 4xx response of some kind: the user made a request that the server can't fulfill. It'd be 400 if the choice was some kind of invalid input or 404 if the location itself just doesn't exist. Although could be 200 if it's a search criteria and there's just no matches.
    – jpmc26
    Jul 21, 2018 at 21:25
12

A few make sense.

403 Forbidden, for the reasons that Eric Stein mentions in his answer. You can use various information provided by the request to determine where the client is and who the client is and, based on that request, the server is unable or unwilling to respond.

However, I would also put forward 451 Unavailable for Legal Reasons as a possible return status for some cases. This status does expect you to include (in the headers) a link to the relevant legislation. It's specifically for cases where it is not legal for the client to be accessing your resources, and not a more general case of the client exists in an unsupported region or area.

I would avoid the 5xx series of statuses - these often indicate server side technical issues. It does not appear to be the case here.

3
  • Probably the reason in this case is that it makes no sense to tell user about cars which are not in their city. So it is not the case for 451
    – max630
    Jul 12, 2018 at 13:37
  • 4
    @max630 You can't assume that from the question. 403 Forbidden is most likely the right choice. However, if there are legal restrictions on the service, the more specific 451 can be used instead. 451 is often seen as a more specific version of 403 for very particular use cases, so it makes sense to bring it up.
    – Thomas Owens
    Jul 12, 2018 at 13:39
  • 8
    @max630 - it's entirely possible that 451 is appropriate here. Many jurisdictions require operators of this kind of service to be registered with regional authorities before providing the service. They could actually be legally prohibited from processing certain requests if they originate out-of-area.
    – Jules
    Jul 12, 2018 at 16:47
5

If the restriction is due to legal reasons, then the appropriate HTTP error code is HTTP 451, "Unavailable due to legal reasons."

This is typically used in the case of material that has been revoked due to DMCA action or lawsuits due to harassment campaigns or the like, but the spirit and letter of the response definition states:

This document specifies a Hypertext Transfer Protocol (HTTP) status code for use when resource access is denied as a consequence of legal demands.

The code itself is a reference to Fahrenheit 451 by Ray Bradbury.

4

People often forget that HTTP status codes are extensible.

HTTP status codes are extensible. HTTP applications are not required to understand the meaning of all registered status codes, though such understanding is obviously desirable. However, applications MUST understand the class of any status code, as indicated by the first digit, and treat any unrecognized response as being equivalent to the x00 status code of that class, with the exception that an unrecognized response MUST NOT be cached. For example, if an unrecognized status code of 431 is received by the client, it can safely assume that there was something wrong with its request and treat the response as if it had received a 400 status code. In such cases, user agents SHOULD present to the user the entity returned with the response, since that entity is likely to include human- readable information which will explain the unusual status.

https://www.rfc-editor.org/rfc/rfc2616#section-6.1.1

You can always just create your own status code in the 400 range for use by your API and client application.

2
  • 1
    Hmm... may not be necessary if the request included an Expect token;city="Albequerque" header. Then the most appropriate response would be 417, expectation failed. tools.ietf.org/html/rfc2616#section-10.4.18 Assuming of course that this is for a web service. Jul 15, 2018 at 12:46
  • Maybe not necessary @BerinLoritsch, but rather than try to force fit a situation into an existing code, it may be better to just punt and use a custom one.
    – RubberDuck
    Jul 15, 2018 at 12:59
0

At first I thought 503 because the description "service unavailable" seems to align with the issue but looking at the definitions, 503 is really specific to server unavailability. Then thinking more, you are telling the client that there is a problem with the request, not that there is a server side issue.

403 is closer because you are telling the user that you received the message and understand it but that the server is unwilling to satisfy it. This might be confusing so a textual explanation can be added to describe the scenario. Per the RFC, 404 is also a valid substitute for this code.

Unless someone has dreamed up a new code for this, 403 or 404 seem to be the closest.

1
  • Definitely not a 5xx code, because that implies that trying the exact same request later might succeed. A 4xx code definitely tells the client not to try again without changing at least some of the request (in this case, the "service location" field). Jul 12, 2018 at 15:07
0

You should match the description of the error with the code that you are giving:

  • if you say Service not available in your area. then you should give a 404 because you claim that the service is not available.

  • if you say You are not authorized for this service in your area. then you should give a 403 because you claim that the caller is not authorized.

I'd go for the second.

6
  • 6
    404 is not about availability, it's about existence. Just because a service is not available in some circumstances you shouldn't provide a 404 response unless you want people to believe that it does not exist at all. A 404 response would be meaningless for this situation.
    – Jules
    Jul 12, 2018 at 16:51
  • @jules According the spec for 403 (which could be out of date): "If the server does not wish to make this information available to the client, the status code 404 (Not Found) can be used instead." emphasis mine. So if 403 is OK, then 404 would also be, but not necessarily for the reason given.
    – JimmyJames
    Jul 12, 2018 at 17:57
  • @JimmyJames - yes, it's within the specification, but is a really bad idea because it makes debugging problems extremely difficult. Not what you want in an API.
    – Jules
    Jul 12, 2018 at 19:28
  • 3
    @Jules The service doesn't exist in some areas. Much like there are many small stores that only exist in my city, so asking their address in another city the correct response is "doesn't exist."
    – Andy
    Jul 13, 2018 at 0:04
  • ehmm talking about "existance" about a service url path is the least - philosophical, for the sake of clarity once you publish a path you must provide an answer, 403 is the way to go in this case, with some kind of verbal description of the situation.
    – Edoardo
    Jul 13, 2018 at 9:13
0

There's a current Internet-Draft (that will expire on December 31, 2018) that proposes amendments to the HTTP 451 Unavailable for Legal Reasons status. The draft suggests that a 451 response should contain a geo-scope-block header that should "correspond to comma-separated list of alpha-2 country codes defined in [ISO.3166-1]". However, the draft also specifies that the code 451 should not be used "by an operator to deny access to a resource on the basis of a policy specified by the operator (as opposed to a legal demand being placed on the operator)".

So assuming you don't have a legal demand for the geoblock, 451 is not the correct code. What is the correct code then? Well, numerous other answers have already suggested 403 Forbidden, but they all seem to be "opinion based", so let's see what others are doing:

So, there's no one universal solution, you are just going to have to pick one that you feel suits your situation the best. But whichever you choose, be sure to explain the actual issue in the response body.

I'd say there would be nothing wrong in just specifying a custom HTTP status code, like RubberDuck already answered. A custom status code in the 400-range might actually even be a pretty good call, because that will definitely get the developers' attention if they see something like "HTTP status 499". A "403" is too easy to pass as "OK so I got my password wrong, let's try something else instead", and that results in wasted hours.

Not the answer you're looking for? Browse other questions tagged or ask your own question.