Third party APIs are a great way to add functionality to your application. However, you are limited to what they offer and in most cases there’s no way to control the rate at which you can call them. Even AWS doesn’t offer any way to limit the number of calls you can make to their APIs.

This is where AWS API Gateway comes in. It allows you to create a proxy for third party APIs, control access with your own API keys and apply rate limits for each key. You can even transform the request and/or response before sending it to the third party API.

In my case I’ve been working on a service that uses OpenAI’s Dall-E API to generate images, users can then call the service using my OpenAI key, but the issue is that I don’t want to share my key with everyone. I also want to limit the number of calls each user can make to the API to prevent abuse and to make sure I don’t go over my monthly limit.

In the next section we’ll go over how to create an API Gateway proxy for OpenAI, but you can use the same steps for any other third party API.

TL;DR

If you just want to try it out with OpenAI you can click the following button to deploy the CloudFormation stack to your AWS account.

Launch Stack

the code is also available on GitHub.

Create an API Gateway proxy for OpenAI using CloudFormation

We could use the AWS console directly and start creating resources, but it’s much better to use CloudFormation to manage infrastructure as code. It’s repeatable and can be versioned using git.

Here’s the CloudFormation template we’ll be using:

Parameters:
  OpenAIKey:
    Type: String
    Description: The OpenAI API key
  QuotaLimit:
    Type: Number
    Description: The quota limit for the usage plan
    Default: 1000
  ThrottleBurstLimit:
    Type: Number
    Description: The burst limit for the throttle settings
    Default: 200
  ThrottleRateLimit:
    Type: Number
    Description: The rate limit for the throttle settings
    Default: 100

Resources:
  OpenAIApiGateway:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: OpenAIApiGateway

  OpenAIApiResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      RestApiId: !Ref OpenAIApiGateway
      ParentId: !GetAtt OpenAIApiGateway.RootResourceId
      PathPart: '{proxy+}'

  OpenAIApiMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref OpenAIApiGateway
      ResourceId: !Ref OpenAIApiResource
      HttpMethod: ANY
      AuthorizationType: NONE
      ApiKeyRequired: true
      RequestParameters:
        method.request.path.proxy: true
      Integration:
        Type: HTTP_PROXY
        IntegrationHttpMethod: ANY
        Uri: 'https://api.openai.com/v1/{proxy}'
        PassthroughBehavior: WHEN_NO_MATCH
        RequestParameters:
          'integration.request.path.proxy': 'method.request.path.proxy'
          'integration.request.header.Content-Type': "'application/json'"
          'integration.request.header.Authorization': !Sub
            - "'Bearer ${OpenAIKey}'"
            - OpenAIKey: !Ref OpenAIKey
        IntegrationResponses: 
          - StatusCode: 200

  OpenAIApiKey:
    Type: AWS::ApiGateway::ApiKey
    Properties:
      Name: OpenAIApiKey
      Description: API Key for OpenAI API Gateway
      Enabled: true

  OpenAIApiUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    Properties:
      ApiStages:
        - ApiId: !Ref OpenAIApiGateway
          Stage: default
      Description: Usage plan for OpenAI API Gateway
      Quota:
        Limit: !Ref QuotaLimit
        Period: MONTH
      Throttle:
        BurstLimit: !Ref ThrottleBurstLimit
        RateLimit: !Ref ThrottleRateLimit

  OpenAIApiUsagePlanKey:
    Type: AWS::ApiGateway::UsagePlanKey
    Properties:
      KeyId: !Ref OpenAIApiKey
      KeyType: API_KEY
      UsagePlanId: !Ref OpenAIApiUsagePlan

  OpenAIApiDeployment:
    Type: AWS::ApiGateway::Deployment
    DependsOn: OpenAIApiMethod
    Properties:
      RestApiId: !Ref OpenAIApiGateway

  OpenAIApiStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      StageName: default
      RestApiId: !Ref OpenAIApiGateway
      DeploymentId: !Ref OpenAIApiDeployment

  OpenAIApiUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    Properties:
      ApiStages:
        - ApiId: !Ref OpenAIApiGateway
          Stage: !Ref OpenAIApiStage

Outputs:
  OpenAIApiUrl:
    Description: The URL for OpenAI API Gateway
    Value: !Join 
      - ''
      - - 'https://'
        - !Ref OpenAIApiGateway
        - '.execute-api.'
        - !Ref 'AWS::Region'
        - '.amazonaws.com/default'

Let’s go over each section and see what it does.

Parameters

The first section defines the parameters we’ll be using in the template. We’ll be using the OpenAI key to authenticate with the API, and we’ll also define the quota and throttle limits for the usage plan. Only the OpenAI key is required, the other parameters have default values.

Resources

This section defines the resources we’ll be creating. We’ll create an API Gateway REST API, a resource, a method, an API key, a usage plan, a usage plan key, a deployment and a stage.

Outputs

The last section defines the outputs for the stack. We’ll be using the API URL to test the proxy.

Using the proxy

Once the stack is created you can go to the API Gateway console and find the URL for the API, then go to the usage plan and find the API key or generate a new one.

Since we’re using OpenAI’s API as an example, the following javascript code will generate an image using the proxy:

import OpenAI from 'openai';

const openai = new OpenAI({
    baseURL: 'API_GATEWAY_PROXY_URL', // Looks like https://xxxxxx.execute-api.us-east-1.amazonaws.com/default
    defaultHeaders: {
        'x-api-key': 'API_KEY_FROM_USAGE_PLAN'
    }
});

async function main() {
    const imageCompletion = await openai.images.generate({
        model: 'dall-e-3',
        prompt: 'an image of a cute cat',
    })

    console.log('imageCompletion', imageCompletion);
}

main();

and just in case you’re wondering what the image looks like:

Cute cat

Conclusion

The API Gateway service is very powerful and offers so much more than what we covered in this tutorial. For further reading I recommend checking out the official documentation.