GET vs POST, and sending parameters

How inputs actually travel to an API. Query strings and the characters that silently break them, JSON bodies and the header they need, and how to tell which method an API wants.

Here is a bug that has shipped in a lot of real software. A signup form checks email addresses with an API, and it works for every address anyone tests. Then a user signs up with [email protected], a perfectly valid address, and the check reports something odd about an address with a space in it.

Nothing is wrong with the API. The + never arrived. To see why, and to stop it happening in your own code, you need to know how parameters travel inside a request. That is this lesson.

Two places to put your inputs

An HTTP request can carry inputs in two main places:

  1. The URL, in the query string after the ?. This is what every call so far has used.
  2. The body, a block of data sent after the headers. A body can hold far more than a URL and can carry structured data like nested objects and lists.

Which one an API uses is tied to the method, the verb at the start of the request:

  • GET means "read". Inputs go in the query string. There is no body.
  • POST means "here is some data, process it". Inputs go in the body, usually as JSON.

There are other methods (PUT, PATCH, DELETE) that you will meet in APIs that store records for you. For the APIs in this course, GET and POST cover everything.

GET and the query string

The Unit Converter API takes three inputs, value, from and to, all as query parameters:

curl 'https://api.apiverve.com/v1/unitconverter?value=5&from=km&to=mi' \
  -H "x-api-key: $APIVERVE_API_KEY"
{
  "status": "ok",
  "error": null,
  "data": {
    "result": { "result": 3.11, "from": "5 km", "to": "mi" },
    "unitDefinitions": {
      "from": { "abbr": "km", "measure": "length", "system": "metric", "singular": "Kilometer", "plural": "Kilometers" },
      "to": { "abbr": "mi", "measure": "length", "system": "imperial", "singular": "Mile", "plural": "Miles" }
    }
  }
}

The structure of the query string is simple: ? starts it, each parameter is name=value, and & separates them. The order does not matter, so ?to=mi&from=km&value=5 asks exactly the same question.

Try it liveUnit Converter1 credit
GEThttps://api.apiverve.com/v1/unitconverter
curl "https://api.apiverve.com/v1/unitconverter?value=5&from=km&to=mi" \
  -H "x-api-key: YOUR_API_KEY"
Sign in or create a free account to run this with your own key.

The converted number is at data.result.result. Swap from and to and run it again.

Everything in a query string is text

A URL has no types. value=5 sends the characters 5, and the server converts them to a number because it knows value is numeric. The same goes for booleans: a parameter documented as true/false arrives as the four letters true or the five letters false. This matters less for GET than for POST, as you will see shortly, but it explains why a query string can never send a list or a nested object without some agreed convention on top.

URL encoding, and the characters that break things

Back to [email protected]. In a query string, several characters already have a job:

CharacterIts job in a URLWrite it as
&Separates parameters%26
=Separates a name from its value%3D
+Means a space in a query string%2B
spaceNot allowed in a URL at all%20 (or +)
#Starts the fragment, which is never sent to the server%23
?Starts the query string%3F

So [email protected] does not mean what it looks like. The + is read as a space, and the server receives jane.doe [email protected]. The fix is URL encoding (also called percent-encoding): replace each special character with % followed by its code. Encoded, the address is jane.doe%2Bnews%40gmail.com and arrives intact. (@ becomes %40; it is usually tolerated unencoded, but an encoder will escape it to be safe.)

The other characters fail in their own ways. An & inside a value, as in Fish & Chips, splits it into two parameters and the second half is lost. A # silently cuts off everything after it before the request even leaves your machine.

You should almost never type these codes yourself. Every language has an encoder, and the rule is: build query strings with the encoder, never by gluing strings together.

const KEY = process.env.APIVERVE_API_KEY;
if (!KEY) throw new Error("APIVERVE_API_KEY is not set");

const url = new URL("https://api.apiverve.com/v1/emailvalidator");
url.searchParams.set("email", "[email protected]");
console.log(url.href); // ...emailvalidator?email=jane.doe%2Bnews%40gmail.com

const res = await fetch(url, { headers: { "x-api-key": KEY } });
const { data } = await res.json();
console.log(data.isSubaddressed, data.normalizedEmail);

Both print true (or True) and [email protected]. The validator recognises the +news part as a sub-address, a tag Gmail ignores when delivering, and gives you the normalised address you would use to spot the same person signing up twice. None of that works if the + is lost on the way.

Try it liveEmail Validator20 credits
GEThttps://api.apiverve.com/v1/emailvalidator
curl "https://api.apiverve.com/v1/emailvalidator?email=jane.doe%2Bnews%40gmail.com" \
  -H "x-api-key: YOUR_API_KEY"
Sign in or create a free account to run this with your own key.

isSubaddressed is true and normalizedEmail drops the +news tag. The Run button encodes the value for you.

From curl, -G with --data-urlencode does the encoding:

curl -G 'https://api.apiverve.com/v1/emailvalidator' \
  --data-urlencode '[email protected]' \
  -H "x-api-key: $APIVERVE_API_KEY"

--data-urlencode normally builds a body; -G tells curl to put it in the query string of a GET instead.

Check yourself

You build a URL by hand: [email protected]. What does the server receive as the email?

Answer the check above to continue.

Run every example on your own key. Free plan, no card.

Scaling up?

Volume pricing, custom SLAs, and dedicated support for high-traffic teams.

Contact sales