This guide explains how to acheive a basic server-side API integration.

This will validate that the user on your website has the right to access the current URL and will redirect them to a waiting room if not.

This guide is intended for developers. It assumes familiarity with APIs etc.

This integration uses the public resources in the CrowdHandler API, using the public key. It's available to any plan, including the free tier.

1. Connecting to the API

First you will need your public key. Read our introduction to the API to get access to your api key.

2. Making your first call

POST /requests/

We're using Postman here to demonstrate the calls. It's a great way to make API calls, to test sending parameters and understand what's coming back as you're putting your integration together.

We're using Basic Auth to send our key. Postman will allow us to enter this once under the authorisation tab, and save it for all the calls we're making. The key goes into the Username field. The Password is ignored. 

Now we have our authentication header set up we're going to start by making a POST call to This call is used to initiate a new request to a URL on your site, to see if this user should get access or be redirected. 

The post call expects the following JSON payload:

"url": "",
"ip": "",
"agent": "Just testing",
"lang": "Just testing"

Let's walk through these parameters:

  • url - This is the url that your user is trying to access. Where you get this from depends upon your application, language and framework. It may be a CGI variable, or you may have some kind of front-controller/router. The URL needs to be complete, including protocol. CrowdHandler only supports HTTPS. 
  • ip - This is the IP address of the user's request. Again, where you retrieve this information from will depend upon your language and framework. IP is required. It's used for fraud-prevention and bot-detection.
  • agent - this is the user agent string. It's also required. You should retrieve it from the User-Agent HTTP header.
  • lang - this is the users preferred language. It is an optional parameter, and can assist with identifying fraudulent agents. You can get it from the HTTP header Accept-Language.

If all's well, and the url you provided is protected by a waiting room, you should receive a response that looks something like this:

    "result": {
        "status": 1,
        "token": "tok_6ihQ7XNw0GmS",
        "title": "Queen Victoria Hall",
        "position": 648,
        "promoted": 0,
        "urlRedirect": null,
        "onsale": "2020-10-21T15:11:11Z",
        "message": "Our website is very busy right now. Sorry to keep you waiting, we will redirect you as soon as possible.",
        "slug": "queen-victoria-hall",
        "priority": null,
        "priorityAvailable": 0,
        "logo": null,
        "responseID": "1dc0784ea988c4139f406061f39edd39",
        "ttl": 0

Walking through the output:

  • token token is the identifier for the user. We didn't have one to begin with, so we made a POST request, which does not require one, and this means we received a new token with our first hit.
  • promoted is the main attribute that we are interested in. A value of 0 means that the user with this token needs to queue for the specified url. A value of 1 means that the user does not need to queue. This token has promoted value 0 which indicates we need this user to queue. 
  • slug the slug, indicates the address of the waiting room. The full address of the waiting room will be
  • responseID is a unique id that identifies the response you just received. We may use this later, to log the performance of this request with the CrowdHandler API.
  • the other attributes are interesting, and may assist with troubleshooting and debugging, but you don't need to use any of them for a simple integration. If you're interested in using them for other reasons you can look up their meanings in the API documentation. 

So if our integration received this particular response, we would want to redirect this user to the waiting room:

Seeing something else?

You may not be getting the model response above. If your response looks significantly different, one of the following things may have gone wrong depending upon what you are seeing: 

  • An error? This would indicate that there is something wrong with your request parameters. The error should tell you what you're doing wrong.
  • No Waiting Room Information? If you're sending URLs that don't fall under the scope of any of your waiting rooms, you will receive a token and promoted will have the value of 1, but you won't get waiting-room information back. You could try setting up a catch-all waiting room, to assist with your testing.
  • No token? If you try testing the call using URLs on domain names that aren't even registered with your CrowdHandler account, you will still receive 1 as the promoted value (we're not responsible for protecting that domain, so as far as we're concerned you're good to go!). But you will not receive a token. 
  • If you are receiving status 3, this means that your IP has been blocked. This can happen when you make lots of public API requests from the same IP. and is common when testing an integration. Log into the admin panel, find your IP (under domains > IPs) and change the status on your IP to ignore

OK, so we made our first call, we know what the output is and how we're supposed to react to it. We'll talk about the specifics of handling this information after we cover the next call.

3. Your second call

GET /requests/:token

In the first example we had no user token. The POST call does not receive a token, it gives you one back. So that's the call to make when the user on your site has no token. But if the user has landed on your site because they were in the waiting room and made it through, then they will have a token. In that case, you want to make sure you use it and retain it to check them. Because if you give them a new one, they'll most likely end up back in the queue. 

Where do I find the token?

When CrowdHandler redirects users to your site it appends the token to the url. It goes in as a query string parameter called ch-id. E.g. So you need to check that URL parameter for a token. And if you find it there, you make the GET call instead of the POST call.

But that's not all!

Your user will then go on to click lots of other links on your website. So to make sure the user does not find themselves back in the waiting room, you need to set a session cookie, whenever you receive the token back, and you need to check the cookie, whenever you can't find ch-id in the URL. If you still don't find one - send the POST request.

Do the tokens expire?

They do. But whenever you pass an invalid token, CrowdHandler will ignore that token, treat the request as if no token was set, and send you back a new token as well as checking your request. This means tokens clean themselves up and you don't need to worry about tracking and expiring tokens. But it also means the token you get back is not necessarily the one you sent. You need to trust the token you receive over the one you sent, so set the cookie every time.

Should I check the cookie first or the URL parameter?

The URL parameter. In complex situations it's possible your user could have a new token in the waiting room, but still have an old cookie stored with your site. If the user is sent from the waiting room, the new token will be in the URL, you should trust that token, over the potentially outdated one that you previously set as a cookie. 

Of course, you should not use the absence of the ch-id parameter as an instruction to wipe any existing cookie. So check carefully.

So then... the call:

The payload is the same as for the POST request, but because this is a GET, we send the parameters as GET parameters, not raw JSON. The URL has changed. This time the token forms part of the URL.

The response, looks very similar to the POST request. Except that this time we can see that the token that we've received is the same one we sent. The decisions our integration needs to take, based on this data, is exactly the same as for the POST request.

In Conclusion

The POST call and the GET call will return the same information. You make the POST call when you have not received a token from the user, and you make the GET call when you have. You do not make these calls in sequence, they are alternatives. Either way the response will be in the same format, and the action you take will be the same. 

You need to store the token in a cookie, or your session object, so that you can validate the user repeatedly as they access different urls on your site.

Take care setting your token cookie or session, because a waiting user may try hitting other urls on your site and you don't want to give them a new position at the back of the queue. For this reason:

1. We recommend storing the token at the earliest opportunity, not waiting until you know if this user should be waiting or not.
2. Be careful about session expiry. Your application may have a session store with a 20 minute timeout, but the user could be in the waiting room for hours. A session cookie that only expires when the user closes their browser is a good bet. A permanent cookie may be even better.

4. When to make your first call

The purpose of CrowdHandler is to protect your site from excessive load. The purpose of making the validation check is to ensure the user on your site should actually be accessing this URL at this time. So you should make these calls at the earliest possible opportunity in your code execution path. The entire purpose is to save on server execution time and resources. As soon as you know the url request and are able to make the call you should make it.

5.  What to do with the result

  • If promoted is 1? then exit the check code and revert to your standard application flow. Serve the URL as you would normally.
  • If promoted is 0? then redirect the user to the waiting room using the slug. Use a 302 HTTP redirect in case the 'user' is a legitimate bot, this tells the bot that the url redirection is temporary.
  • If you get a malformed response, or the api call fails... CrowdHandler has many layers of protection, but there are reasons this could happen and you need to respond. There could be a temporary network issue in your data centre or on an internet backbone. If the process of making the API call fails, or the process of parsing a legitimate result fails, or evaluating the promoted value fails then you need to treat this as an exception and handle it. How you handle it is up to you:
    • Redirect on fail presume that if you don't know better, that the user should be queued. If you know the slug of your standard waiting room (you may have a default catch-all with a stable slug) then you could send the user there. If you don't know, you can send a user to, where the user will be served a generic template, will poll regularly for better information, and will be directed back to your site, or to the appropriate waiting room, when normal service is resumed.
    • Trust on fail - you can decide to let the user have access to the page if the API call fails. This will depend on how regularly your site sees high-traffic and how predictable the patterns are. If you've installed CrowdHandler only for occasional, planned use, with event-specific waiting rooms, you may prefer to to trust users in the event that you are not receiving well-formed responses from the CrowdHandler API.

6. Redirecting a user

When redirecting a user you should provide the following url-encoded parameters in the redirect URL.

  1. ch-id: the token that came back from the API.
  2. url: the url that the user was requesting. If query string parameters are important to you (whether for marketing/tracking reasons, or because you use them for product IDs and similar) then you need to ensure they are passed as part of the url parameter too. 
  3. ch-public-key: if you do not know the slug of the waiting room for your users, perhaps because you received a malformed-response, or no response, then you can send your public key instead of a slug, using this parameter. This will allow CrowdHandler's safety-net waiting room to use your key to look up the correct slug, and handle appropriately.  

7. The Last Call. Logging Page Performance

PUT /responses/:id

Logging the performance of your page allows CrowdHandler to monitor the performance of your domain. This is helpful when monitoring heavy traffic, and critical to enabling the autotune feature.

This call is a PUT. and it goes to a new resource /responses/:id . The ID was provided to you as responseID in the result of the first call you made.

The payload for this call looks something like this:

    "code": 200,
    "time": 2000
  • code is the HTTP response for the page you are serving to the user. It would typically be 200, but if you are able to trap http error codes you should send them. This will help CrowdHandler identify when your website is struggling*, allowing autotune to take action. If you omit this parameter, CrowdHandler will default to 200.
  • time is the time for your page to load in milliseconds. You can calculate this by:
    • taking the time when you first receive your request (a)
    • taking the time just before you submit this PUT (b)
    • subtracting b from a and ensuring the difference in time is expressed in milliseconds (thousandths of a second).

      Whilst this sounds fairly straightforward, you need to take quite a bit of care. Different languages and frameworks have different methods for tracking time in small increments, there can be differences, or performance issues, based on whether you are using VMs or bare-metal servers, and different processor types. Some frameworks have APIs specifically for tracking page performance. If you are confident, you can supply a very accurate reading to CrowdHandler. If you're less confident, simply omit this parameter. In this case CrowdHandler will do its own calculation based on the time between the initial request, and the subsequent PUT. The estimate for page load time could be impacted somewhat by network times, and is lower resolution, but the information is sufficient for a health check and is better than the wildly inaccurate value that could be supplied if you get your calculation wrong.

* a response with an error code, is handled by CrowdHandler in the same way that a request exceeding your maximum response time is. So if you are serving 500 errors very fast, but the quantity of errors is exceeding your acceptable % threshold, autotune will slow down traffic as if those requests were responding in excess of your acceptable page load threshold. 

When to make the call.

Whilst the initial call should be made as early as possible, this call should be made as late as possible, after you've served your page to the user. Also - it sounds obvious, but you only make this call if you actually served the URL. If you redirected the user to waiting room, you should skip this call altogether. So after your first call there is a conditional branch between sending the redirect, or serving the page, and logging the performance.

8. Putting it all together

The following pseudo-code** puts it all together to describe a simple, yet complete, integration. You'll see it gets the idea across more quickly than the lengthy explanations we've given up to now,  but nevertheless you should refer to the notes for context.

chKey = <your public key>
timeA = Timer.getTimeInMs()
request = Controller.getRequest()
response = Controller.startRresponse()
trustOnFail = true

if request.urlParams["ch-id"] 
    # token in query string. We want to set cookie so we recognise this user
    response.cookies["ch-id"] = request.urlParams["ch-id"]
    # and we'll strip ch-id param and redirect to discourage token sharing
    cleanParams = request.urlParams.unset("ch-id")
    response.redirect(request.url, cleanParams, 302)
    # set up the API gateway
    crowdhandler = HttpAPI.create(baseURL="", authentication=chKey)
    params = {"url": request.url, "ip":, "agent": request.userAgent, "lang": request.acceptLang}
    # look for crowdhandler token in the cookie
    token = request.cookies["ch-id"]

    # check this user's request
        if token 
            # user has token
            result = crowdhandler.get("requests/"+token, params)
            # user needs new token
            result ="requests/", params)
        # api call failed!
        if trustOnFail
            # we do nothing
            # we send user to waiting room
            queryParams = {"url": request.url, "ch-id": token, "ch-public-key": chKey}
            response.redirect("", queryParams, 302)

# if we got here, we have a valid response. 
# It's possible that the API has responded with a new token.
token = response.token
response.cookies["ch-id"] = token

if result.promoted
    # this token is good for this url
    # render your page / hand back control / you do you
    response.html = "Your Content"
    # all done? Now to log the performance
    time = timeA - Timer.getTimeinMs()
    crowdhandler.put("/responses/"+result.responseID, {"code": 200, "time": time})
    # the user with this token needs to wait
   response.redirect(""+result.slug+"?", {"url": request.url, "ch-id": token}, 302)

** it's the mongrel love-child of ruby and python, two readable languages, with imaginary, and simple APIs for issuing API calls, retrieving request data and sending responses. We hope it's easy for you to read between the lines.

Take a look at our PHP SDK for a fully working code library.