APIの利用制限対策

もくじ > Web開発ノート > 独自開発CMS+PHP

Youtube Data APIや、Instagram Graph APIなど、SNSの更新状況などをリアルタイムに反映させるAPIは便利ですが、使用回数やアクセス頻度などに制限があります。

Instagram Graph APIは1時間に200回、Youtube Data APIはクォータという単位で制限がかけられており、1日10,000クォータ以内程度に制限されています。これを超える場合は、何かしらの申請が必要だったりします。

この数字はアクセスの少ない個人サイトがAPIのテストで使う程度であれば大丈夫ですが、アクセス数が多いブランドサイトや情報サイトなどでは、とても実用的な数字ではありません。

ここでは、APIの使用制限の範囲内で最大限に活用する方法のひとつをまとめてみました。

APIの利用制限対策の実例

取得データをサーバに保存する

SNSの更新頻度はどれくらいでしょうか?せいぜい1日1回程度?更新が多い人でも1日5回とか6回。10回も更新すればSNS廃人とか言われるレベルです。

それなのに、アクセスする人が期待してリロードするたびに、APIへのアクセス回数が1カウントされます。期待する人が10万人いて、1日に10回リロードしたら、APIに100万回アクセスし、利用制限は公開後一瞬にして利用制限オーバーです。

そこでサーバプログラムが定期的にAPIにアクセスし、結果をサーバ上に保存しておき、ページ表示する時はそのデータの内容を表示するようにしておけば、ユーザーがアクセスするたびにAPIにアクセスする必要がなくなります。

cronを使い、1時間に1回プログラムを実行したとしても、APIへのアクセス回数は1日24回です。

cronを使いたくない場合

サーバ側でcronの設定ができない場合や、開発サーバ、テストサーバ、公開サーバと3タイプのサーバを経由するため、それぞれのサーバに合わせたcronの設定が面倒な場合は、cronを使いたくありません。

そんな時は、PHPが実行された時の時間が、最後にAPIにアクセスした時の時間から指定時間経過していたらAPIにアクセスする。といった仕組みにすれば、cronの設定などしなくてもAPIへのアクセス回数を抑える事ができます。

Youtube Data APIの例

この流れを具体的なソースコードで表わすと、以下のPHPサンプルのようになります。この例では1時間に1回、Youtube Data APIからデータを取得しサーバにJSON形式でデータ保存させています。

<?php

$myChannelId = "【YoutubeチャンネルID】";
$myReferer = "【自分のWebサイトのURL】";
$getCount = "【取得件数】";

$apiKey = "【API KEY】";
$apiUrl = "https://www.googleapis.com/youtube/v3/search";

//APIとの交信間隔の時間(例:1時間おき)
$nextUpdate = 1;

//最後にAPIと交信した時間を保存
$update_url = "lastupdate.json";

//APIからの返信内容を保存
$data_url = "youtubeapi.json";

$part = "snippet";
$loadAllow = false;

//APIの更新時間をチェック
$json = @file_get_contents(__DIR__ . $update_url);
$json = mb_convert_encoding($json, "UTF8", "ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN");
$dataAry = json_decode($json, true);
$update_time = $dataAry["info"]["update"];

//最終更新時間からの経過
$timeUpdate = strtotime($update_time);
$timeToday = strtotime("now");
$diff = floor(($timeToday - $timeUpdate) / 60 / 60);

if($diff >= $nextUpdate){

 //APIと交信

 $data = http_build_query(
  array(
   "part" => "snippet",
   "channelId" => $myChannelId,
   "maxResults" => $getCount,
   "order" => "date",
   "key" => $apiKey
  )
 );

 //リファラー情報
 $header = Array(
  "Content-Type: application/x-www-form-urlencoded",
  "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
 );

 $options = array("http" =>
  array(
   "method" => "GET",
   "header" => implode("\r\n", $header), 
   "header" => "Referer: ".$myReferer."\r\n"
  )
 );

 $json = @file_get_contents($apiUrl . "?" . $data, false, stream_context_create($options));

 //取得したデータが正しいものかどうかチェック
 $loadAllow = false;
 if(strpos($json,"kind") !== false){
  if(strpos($json,"youtube#searchListResponse") !== false){
   if(strpos($json,"items") !== false){
    $loadAllow = true;
   }
  }
 }

 if($loadAllow){

  //APIからのデータ取得に成功したら

  $ret = mb_convert_encoding($json, "UTF8", "ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN");

  //APIからの情報をサーバに保存
  $fileData = fopen(__DIR__ . $data_url, "w+b");
  fwrite($fileData, $json);
  fclose($fileData);

  //交信した時間を保存
  $today_time = date("Y-m-d H:i:s");

  $obj = array(
   "info"=>array(
    "update"=>$today_time
   )
  );

  $data = json_encode($obj);
  $fileData = fopen(__DIR__ . $update_url, "w+b");
  fwrite($fileData, $data);
  fclose($fileData);

 }

}

if(!$loadAllow){

 //APIと交信しない場合はサーバ保存したデータを使う

 $json = @file_get_contents(__DIR__ . $data_url);
 $ret = mb_convert_encoding($json, "UTF8", "ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN");
}

return $ret;

参考API:
Youtube Data API
Instagram Graph API