If you have a public API that your customers integrate into their software or services, you’ll want to implement a rate limiting policy to prevent misuse, ensure fair access for all users, and prevent heavy load from slowing down your entire infrastructure. Based on your packaging and pricing model, you may also need to implement different rate limit limits for customers based on their “tier” and how much they pay. ngrok and Traffic Policy let you validate an incoming user’s request against identity or CRM services with data about their tier and selectively apply the proper rate limits to their IP address.

1. Create endpoints for your services

Start an internal Agent Endpoint, replacing $PORT based on where your upstream service listens. You can also use one of our SDKs or the Kubernetes Operator.
ngrok http $PORT --url https://service.internal
Start a second endpoint for your identity service.
ngrok http $PORT --url https://id.internal

2. Reserve a domain

Navigate to the Domains section of the ngrok dashboard and click New + to reserve a free static domain like https://your-service.ngrok.app or a custom domain you already own. We’ll refer to this domain as $NGROK_DOMAIN from here on out.

3. Create a Cloud Endpoint

Navigate to the Endpoints section of the ngrok dashboard, then click New + and Cloud Endpoint. In the URL field, enter the domain you just reserved to finish creating your Cloud Endpoint.

4. Validate requests against your internal identity service with Traffic Policy

While still viewing your new Cloud Endpoint in the dashboard, copy and paste the policy below into the Traffic Policy editor. You’ll need to change:
  • https://id.internal/api/user: Replace the path according to your identity service’s API.
  • $ID_API_TOKEN: Replace with an API key or other authentication method generated by your identity service.
  • { "user": "${req.headers['Authorization']}" }: Replace with an appropriate body based on how your user’s requests are structured.
on_http_request:
  - actions:
      - type: http-request
        config:
          url: https://id.internal/api/user
          method: POST
          headers:
            Authorization: "Bearer $ID_API_KEY"
            Content-Type: "application/json"
          body: |
            { "token": "${req.headers['Authorization']}" }

  # If the request failed, short-circuit the request with an error response.
  - expressions:
      - "actions.ngrok.http_request.res.status_code < '200' &&
        actions.ngrok.http_request.res.status_code >= '300'"
    actions:
      - type: custom-response
        config:
          headers:
            content-type: application/json
          body: |
            { "message": "Request failed: ${actions.ngrok.http_request.res.status_code}" }

  # If the request was successful, capture the value of the user's tier for reuse.
  - expressions:
      - "actions.ngrok.http_request.res.status_code == 200"
    actions:
      - type: set-vars
        config:
          vars:
            tier: json.decode(actions.ngrok.http_request.res.body).tier.value

  # Check the user's tier based on the variable and apply the rate limit.
  - expressions:
      - "vars.tier == 'bronze'"
    actions:
      - type: rate-limit
        config:
          name: BronzeLimit
          algorithm: sliding_window
          capacity: 30
          rate: 60s
          bucket_key:
            - conn.client_ip

  - expressions:
      - "vars.tier == 'silver'"
    actions:
      - type: rate-limit
        config:
          name: SilverLimit
          algorithm: sliding_window
          capacity: 100
          rate: 60s
          bucket_key:
            - conn.client_ip

  - expressions:
      - "vars.tier == 'gold'"
    actions:
      - type: rate-limit
        config:
          name: GoldLimit
          algorithm: sliding_window
          capacity: 1000
          rate: 60s
          bucket_key:
            - conn.client_ip

  - actions:
      - type: forward-internal
        config:
          url: https://service.internal
What’s happening here? This policy first sends an authenticated HTTP request to your internal identity/CRM service at https://id.internal. This request’s body contains the Authorization token your user added as a header to their request. If your identity service responds with a 400-level error code, the policy short-circuits the request and responds with a custom error response. If your identity service responds with a 200 error code, then the policy saves the user’s tier as a vars.tier variable. The policy then applies different rate limiting capacities to the user’s IP address based on the tier data stored in your identity service. Finally, the policy forwards the authenticated and rate limited request to your upstream service.

Try out your endpoint

Visit the domain you reserved either in the browser or in the terminal using a tool like curl. You should see the app or service at the port connected to your internal Agent Endpoint.

What’s next?