Define subscription plan with different quota

Not applicable

Hi,

I have two API proxies : PHONE_API and DATA_API. I would like to create "subscription plan" for API consumers as follow :

API PLAN A PLAN B
PHONE_API No access Quota : 50 queries / hour
DATA_API Quota : 50 queries / hour Quota : unlimited

For now, I created these two API proxies. I also created two API product (one for each plan).

How could I define multiple quota for one API proxy depending of the API product the consumer has subcribed for ?

Moreover, is it possible to restrict API proxies calls depending of the subscription plan (API product) the consumer has subscribed for ?

What is the best way to achieve this ?

Thanks in advance!

Solved Solved
1 1 252
1 ACCEPTED SOLUTION

is it possible to restrict API proxies calls depending of the subscription plan (API product) the consumer has subscribed for ?

Yes, this is the purpose of the API Product.

The API Product has a data field that allows you to specify the quota for the product. But the quota for the product would be applied across all APIs equally. so if you add both the phone and data API into the product, and set the quota on the product to 50/hour, then it would allow, at runtime, 50 calls per hour in aggregate for requests against either the phone API or the data API. This is not what you want. You need something a little more flexible.

Fortunately the API Product is flexible enough to handle your case. We will use custom attributes on the API product entity to define particular quotas for each API. Also we will just exclude the phone API proxy from one of the products to accomplish the "no access" goal.

Ready? Let's go.

To setup for your scenario, here's what you'll do:

  1. Create an API Product called "Plan A"
  2. Add the "data api" proxy into it.
  3. Set a custom attribute on the product called "data_api_quota" with value 50
  4. Create an API Product called "Plan B"
  5. add the phone api proxy into it. Also add the data API proxy.
  6. Set a custom attribute on the product called "phone_api_quota" with value 50

The next bit is to configure the API Proxies to do the right thing, referencing those custom attributes.

First, in each API proxy (data api, phone api) you will need to add a policy to verify app or client credentials. This is either VerifyApiKey or OAuthV2/VerifyAccessToken.

Either way, after the api proxy has verified credentials, the API Product associated with the credential will be available in "the context" via a context variable. For the rest of this, let's assume you're using OAuth2 tokens.

The OAuthV2/VerifyAccessToken sets these variables upon successful verification:

This policy sets the following flow variables:

  • organization_name
  • developer.id
  • developer.app.name
  • client_id
  • grant_type
  • token_type
  • access_token
  • accesstoken.<custom_attribute_name>
  • issued_at
  • expires_in
  • status
  • scope
  • apiproduct.name
  • apiproduct.<custom_attribute_name>

Most of those are uninteresting for your purposes. The interesting bit is the apiproduct.custom_attribute. Let's first look at the phone API. You will introduce a Condition element into the flow to check for the quota values you want. like this:

<Step>
  <Condition>NOT (apiproduct.phone_api_quota = null)</Condition>
  <Name>Quota-PhoneApi</Name>
</Step>

...which says "enforce the quota on phone API only if the product defines an attribute for a limit".

Inside the quota policy you will reference that variable. Like this:

<Quota name='Quota-PhoneApi'>
    <Identifier ref='client_id' />
    <Allow countRef='apiproduct.phone_api_quota'/>
    <Interval>1</Interval>
    <TimeUnit>hour</TimeUnit>
    <Distributed>true</Distributed>
    <Synchronous>false</Synchronous>
    <PreciseAtSecondsLevel>false</PreciseAtSecondsLevel>
</Quota>

And Then you will do something similar in the data_api proxy.

Done!

Please note: the client_id may not be the right identifier. Using the client_id as the identifier would enforce a limit of 50 (or whatever you choose) across ALL instances of that client. If you deploy the same app to 1000 devices, then in aggregate that set of devices would get 50 calls per hour. Maybe not what you want.

To restrict it to a particular instance of the app, then you would need to use the access_token. The access token will be unique to each app instance. But then you need to concern yourself with the refresh of that token. If the app reaches the quota limit, it could then refresh the token, and get a new (blank) quota. If you don't rate limit the token issuance, then you haven't rate limited your API. Do you see?

The trick is to use an identifier which is unique to the app instance and perhaps doesn't vary over time like the access token. If you are using 3-legged Oauth, then you could potentially use the userid. This will be the same across all "epochs" of access_token. There may be other approaches for other scenarios.

View solution in original post

1 REPLY 1

is it possible to restrict API proxies calls depending of the subscription plan (API product) the consumer has subscribed for ?

Yes, this is the purpose of the API Product.

The API Product has a data field that allows you to specify the quota for the product. But the quota for the product would be applied across all APIs equally. so if you add both the phone and data API into the product, and set the quota on the product to 50/hour, then it would allow, at runtime, 50 calls per hour in aggregate for requests against either the phone API or the data API. This is not what you want. You need something a little more flexible.

Fortunately the API Product is flexible enough to handle your case. We will use custom attributes on the API product entity to define particular quotas for each API. Also we will just exclude the phone API proxy from one of the products to accomplish the "no access" goal.

Ready? Let's go.

To setup for your scenario, here's what you'll do:

  1. Create an API Product called "Plan A"
  2. Add the "data api" proxy into it.
  3. Set a custom attribute on the product called "data_api_quota" with value 50
  4. Create an API Product called "Plan B"
  5. add the phone api proxy into it. Also add the data API proxy.
  6. Set a custom attribute on the product called "phone_api_quota" with value 50

The next bit is to configure the API Proxies to do the right thing, referencing those custom attributes.

First, in each API proxy (data api, phone api) you will need to add a policy to verify app or client credentials. This is either VerifyApiKey or OAuthV2/VerifyAccessToken.

Either way, after the api proxy has verified credentials, the API Product associated with the credential will be available in "the context" via a context variable. For the rest of this, let's assume you're using OAuth2 tokens.

The OAuthV2/VerifyAccessToken sets these variables upon successful verification:

This policy sets the following flow variables:

  • organization_name
  • developer.id
  • developer.app.name
  • client_id
  • grant_type
  • token_type
  • access_token
  • accesstoken.<custom_attribute_name>
  • issued_at
  • expires_in
  • status
  • scope
  • apiproduct.name
  • apiproduct.<custom_attribute_name>

Most of those are uninteresting for your purposes. The interesting bit is the apiproduct.custom_attribute. Let's first look at the phone API. You will introduce a Condition element into the flow to check for the quota values you want. like this:

<Step>
  <Condition>NOT (apiproduct.phone_api_quota = null)</Condition>
  <Name>Quota-PhoneApi</Name>
</Step>

...which says "enforce the quota on phone API only if the product defines an attribute for a limit".

Inside the quota policy you will reference that variable. Like this:

<Quota name='Quota-PhoneApi'>
    <Identifier ref='client_id' />
    <Allow countRef='apiproduct.phone_api_quota'/>
    <Interval>1</Interval>
    <TimeUnit>hour</TimeUnit>
    <Distributed>true</Distributed>
    <Synchronous>false</Synchronous>
    <PreciseAtSecondsLevel>false</PreciseAtSecondsLevel>
</Quota>

And Then you will do something similar in the data_api proxy.

Done!

Please note: the client_id may not be the right identifier. Using the client_id as the identifier would enforce a limit of 50 (or whatever you choose) across ALL instances of that client. If you deploy the same app to 1000 devices, then in aggregate that set of devices would get 50 calls per hour. Maybe not what you want.

To restrict it to a particular instance of the app, then you would need to use the access_token. The access token will be unique to each app instance. But then you need to concern yourself with the refresh of that token. If the app reaches the quota limit, it could then refresh the token, and get a new (blank) quota. If you don't rate limit the token issuance, then you haven't rate limited your API. Do you see?

The trick is to use an identifier which is unique to the app instance and perhaps doesn't vary over time like the access token. If you are using 3-legged Oauth, then you could potentially use the userid. This will be the same across all "epochs" of access_token. There may be other approaches for other scenarios.