How to check API endpoint

One request, bounded and scriptable: curl -sf --connect-timeout 3 --max-time 10 -o /dev/null -w '%{http_code}\n' https://api.example.com/health. -f makes any 4xx or 5xx exit 22, the timeouts stop the probe hanging, and the printed code tells a human what the exit code alone does not.

Why check this

This is the first command to run when a test suite starts failing everywhere at once, and the shape of the probe a monitor or a CI smoke stage runs. It separates "the service is gone" from "the service answered something I did not expect", which are different tickets. Without the timeouts it also prevents a slow endpoint from freezing a pipeline stage until the job limit kills it.

Prerequisites

Steps

  1. Step 1.

    Probe the endpoint once with a time budget.

    curl -s -o /dev/null --max-time 5 -w 'code=%{http_code} time=%{time_total}\n' https://httpbin.org/get
    
    code=200 time=0.624504
  2. Step 2.

    Point the same probe at a host that does not resolve and read curl's exit code.

    curl -s --max-time 5 -o /dev/null https://api.h2check-does-not-exist.org/health; echo "exit=$?"
    
    exit=6

    Exit 6 is a DNS failure. The service may be running; the name is what is missing.

  3. Step 3.

    Give the probe less time than the endpoint needs.

    curl -s --max-time 2 -o /dev/null https://httpbin.org/delay/5; echo "exit=$?"
    
    exit=28

    Exit 28 is the timeout curl enforced, not a verdict from the server.

  4. Step 4.

    Turn an HTTP error into a failed command with -f, keeping the code visible.

    curl -sS -f --connect-timeout 3 --max-time 5 -o /dev/null -w 'code=%{http_code}\n' https://httpbin.org/status/503; echo "exit=$?"
    
    curl: (22) The requested URL returned error: 503
    code=503
    exit=22

    -sS hides the progress meter and keeps the error line, so a CI log still says what happened.

  5. Step 5.

    Run the monitor shaped command against a real API, with retries for a single dropped packet.

    curl -sf --retry 2 --retry-delay 1 --connect-timeout 3 --max-time 10 -o /dev/null -w 'code=%{http_code} time=%{time_total} bytes=%{size_download}\n' https://api.github.com/; echo "exit=$?"
    
    code=200 time=0.194663 bytes=2396
    exit=0
  6. Step 6.

    Probe a route that returns 200 and no content, to see what the status hides.

    curl -s -o /dev/null -w 'code=%{http_code} bytes=%{size_download}\n' https://httpbin.org/status/200
    
    code=200 bytes=0

    A status only probe passes on an empty body. So does a health route that answers before it queries anything.

  7. Step 7.

    Assert on the payload, not on the code.

    curl -sf --max-time 5 https://httpbin.org/json | grep -q '"slideshow"' && echo "up" || echo "down"
    
    up

How to read the result

| What you see | What it means | What to do | | --- | --- | --- | | exit=0 with a 2xx code | The endpoint answered inside the budget | Move on to the payload assertion in step 7. | | exit=6 | The host name did not resolve | Check DNS and the environment variable holding the base URL before touching the service. | | exit=28 | curl cut the request at --max-time | Raise the budget once to find the real duration, then decide whether the endpoint or the budget is wrong. | | exit=22 with a 5xx code | The service is reachable and broken | A server side defect. The reachability part of the probe passed. | | code=200 bytes=0 | A 200 carrying nothing | The route answers, the feature is unproven. Assert on content. | | code=000 | No response arrived | Read the exit code. The status field has nothing to report. |

Common mistakes

Sign: The health endpoint returns 200 all night and users cannot log in.Cause: A health route that answers from the web layer never touches the database, the cache or the queue. Step 6 shows a 200 with zero bytes passing a status only probe. Probe a route that does the work, or assert on a payload field the dependency produces.
Sign: A monitor run stacks up and the machine runs out of sockets.Cause: curl has no default timeout, so a hung endpoint keeps the connection open until the operating system gives up. Set --connect-timeout and --max-time on every scripted probe, both below the interval the monitor uses.
Sign: The probe reports up while the API returns a login page.Cause: With -L, curl follows the redirect and reports the code of the last hop, so a 302 to an HTML sign in page reads as 200. Drop -L in a liveness probe, or assert that the response body still contains a field only the API returns.

What to check next

FAQ

How to test an API endpoint in the browser?

Paste the URL in the address bar for a GET, then read the code in DevTools, Network tab, Status column. The browser sends cookies and an Origin the API may treat differently, so a result that differs from curl is a finding, not noise.

Which curl exit codes matter in a liveness script?

Four cover almost every case seen here: 0 for a completed request, 6 for a name that does not resolve, 28 for the --max-time cut, and 22 for an HTTP error when -f is set. Branch on those and log the rest.

Does a 200 prove the endpoint is up?

It proves something answered with a status line. It does not prove the response has a body, that the database was reached, or that the answer is not a cached copy. Step 6 returned 200 with zero bytes.

Should the probe follow redirects?

Not for liveness. Following a redirect reports the code of whatever the last hop returns, which hides a route that now points at a sign in page. Keep the probe on one hop and read location when the code is 3xx.

Verified

Verified by Maks Vernycurl 8.21.0

Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.

basic5 minpublished updated Maks Verny