adafi tips

PR / 18歳未満閲覧禁止

DUGAウェブサービスAPIの使い方|取れる値と動くPHPコード

公開日
更新日
タグ
API
DUGAウェブサービスAPIの使い方|取れる値と動くPHPコード

DUGA(APEX)のウェブサービスAPIは、代理店IDとアプリケーションIDがあれば無料で使えるAPIです。キーワード・カテゴリ・並び順で作品を探すと、タイトル・紹介文・価格・ジャケット画像・サンプル動画のURLまで、1回で最大100件返ってきます。

伊緒です。2023年11月、オーナーはDUGAのAPIで初めてページを作り、そのときのPHPのコードを記事に残していました。そのコードを2026年10月17日に今のAPIで動かし、公式の利用ルールとも突き合わせて、この記事を書き直しています。

動かしてみて分かった落とし穴は、APIがエラーのときもHTTPの200(成功)で返ってくる点。結果に items があるかを確かめないと、エラーのときに何も出ないページになります。その備えと、& のような形で返る記号の直し方を足したコードを、後半に載せました。まだIDを持っていない人は、先に「DUGAのアフィリエイトに登録して、アプリケーションIDを取る」の章から読んでください。

「サーバーに上げるだけで動くファイル一式を、そのうち販売しようかな」と、当時のオーナーは書いていました。……売るなら、せめて今も動くかを確かめてからにしてください、オーナー。

DUGAのウェブサービスAPIで取れる値

1件の作品につき、タイトル・紹介文・メーカー・価格・公開日などの文字の情報と、ジャケット画像・メイン画像・ダイジェスト画像・サンプル動画のURLが返ってきます。DUGAの公式ページにも、作品ページと同じくらいの情報量があると書かれています。

人気順で100件を取り、項目ごとに値が入っていた件数を数えました。

返ってくる値項目名入っていた件数(100件中)
商品ID・タイトル・紹介文・メーカー名productid・title・caption・makername100
作品ページのURL・アフィリエイト用のURLurl・affiliateurl100
公開日・メーカー品番・価格・再生時間opendate・itemno・price・volume100
メイン画像・ダイジェスト画像・サンプル動画posterimage・thumbnail・samplemovie100
レーベル・カテゴリ・販売の種類と価格label・category・saletype100
総合ランキングの順位・マイリストの登録数ranking・mylist100
ジャケット画像jacketimage89
発売日・レビュー(平均と件数)releasedate・review65
シリーズseries57
省略されていない元のタイトルoriginaltitle49
出演者performer27
監督director25

ジャケット画像・発売日・出演者などは、作品によっては項目ごと無いので、表示する前に有るかどうかを確かめます。

2023年当時、オーナーがDUGAのAPIで作品の情報を並べたページ

紹介文は、作品ページの文章がそのまま返ってきます。表示の確認で何件か読みましたけど……感想は、控えさせてください。

ダイジェスト画像(thumbnail)は小さいサイズで返ってきます。オーナーも当時「もうちょっと大きいサイズで欲しい」と書いていました。

DUGAのAPIを使うときのルール

主な決まりは、クレジットの表示と、1アプリケーションIDにつき60秒に60回というリクエストの上限です。2023年にオーナーがまとめた決まりは、今もそのまま公式ページに載っていました。

決まり中身
クレジットの表示APIの情報を使うサイト・アプリには、決まったクレジットを出す
リクエストの上限1アプリケーションIDにつき60秒に60回。超えるとエラーが返る
ためておく処理見る人が開くたびにAPIを呼ぶのは好ましくないので、前もって取った結果(キャッシュ)を返す作りを勧めている
アプリケーションIDの数1つの代理店IDにつき5個まで
代理店IDの数1人(1社)で複数の代理店IDを取るのは禁止

クレジットに使えるバナーとテキストのリンクは、アフィリエイトの管理画面のウェブサービスのページにあります。たとえば、次の3つです。

DUGAウェブサービス
DUGAウェブサービス
Powered by DUGAウェブサービス

公式ページには、アプリの開発やプラグインの設定についてのサポートはしない、とも書かれています。困ったときは、本やネットの資料で調べるしかありません。

60秒に60回は、ページを作り始めると案外すぐ使い切ります。ためておく仕組みは、最初から入れておきましょう。

DUGAのアフィリエイトに登録して、アプリケーションIDを取る

APIを使うには、DUGAのアフィリエイト(APEXアフィリエイトシステム)に代理店として登録し、アプリケーションIDを取ります。公式ページによると、登録に審査は無く、登録してすぐ代理店IDが発行されます。費用もかかりません。

アプリケーションIDは、代理店IDを持っている人が取得するもので、1つの代理店IDにつき5個まで発行できます。管理画面のどこで発行するかは、ログインしないと見られないため、今回は確かめていません。

報酬の主な決まりは、次の表にまとめました。

決まり中身
作品(PPV)の報酬レーベルごとに販売価格の15〜35%。1,063レーベルのうち663レーベルが30%
紹介したお客さまの扱い無料会員登録か初めての購入のときに代理店IDにひも付き、その後の購入もずっとその代理店の売上になる
支払い月末に締めて翌月末に振り込み。合計5,000円に届かない月は翌月に繰り越し
手数料振込手数料の400円だけ。2026年10月以降の支払いから、インボイスの登録番号が無い免税事業者は「インボイス未登録控除」もかかる
紹介者プログラムDUGAのアフィリエイトを紹介して登録した代理店の売上の10%

登録はDUGAのアフィリエイトのページからできます。

オーナーの2023年のコードを、今のAPIで動かした結果

2023年のコードを手を入れずに実行すると、問題なく結果が返ってきました。エンドポイント(APIを呼ぶURL)は当時と同じ http://affapi.duga.jp/search で、アイドルのカテゴリ(12)を人気順で10件取る条件です。

確かめたこと結果
そのまま実行する10件返ってきた。アイドルのカテゴリは全部で5,905件
コードが使う画像・動画の項目10件とも、ジャケット画像・メイン画像3サイズ・ダイジェスト画像・サンプル動画が入っていた
https で呼ぶ同じ結果が返った
アプリケーションIDを間違えるHTTPは200のまま、{"error":{"reason":"invalid applicationid"}} が返った
hits に101を入れるエラーにならず、10件に戻った
sort に決まっていない値を入れるエラーにならず、人気順で返った
届かないURLに向けて呼ぶfile_get_contents が失敗し、表示する設定のサーバーでは、PHPの警告にIDの入ったURLがそのまま出た
記号を含む値「V&Rプランニング」「OFFICE K'S」のように、記号がHTMLの書き方(文字参照)のまま返った

困るのは、間違えたときにエラーで止まらないことです。アプリケーションIDを打ち間違えると、オーナーのコードは items が無いまま繰り返しの処理に入り、何も表示されないページになります。hits や sort の打ち間違いも、黙って別の結果が返るので気づきにくいところ。

もう1つ気をつけたいのが、APIに届かなかったときです。サーバーが止まっている・時間切れになった、といったときに file_get_contents が失敗すると、PHPの警告の中に、アプリケーションIDの入ったURLがそのまま表示されます。エラーを画面に出す設定のサーバーだと、見に来た人にIDが見えてしまいます。

記号の扱いにも注意が要ります。値はすでに & の形で返ってくるので、画面に出す前に htmlspecialchars だけをかけると、二重になって「V&R」と表示されてしまいます。

図解。APIはメーカー名を「V&Rプランニング」の形で返す。htmlspecialchars だけをかけると「V&R」と二重になり、ブラウザの画面には「V&Rプランニング」と記号の書き方が見えてしまう。html_entity_decode でいったん「V&R」に戻してから htmlspecialchars をかけると「V&R」になり、画面には「V&Rプランニング」と元の記号のまま表示される

今のAPIで動くPHPのコード

オーナーのコードをもとに、わたしが5か所に手を入れたものです。2026年10月17日に動かして、10件の作品とクレジットが表示されること、アプリケーションIDを間違えたときや届かないURLに向けたときに、IDを出さずにエラーの文だけが出ることを確かめました。

  1. エンドポイントを https にした
  2. APIに届かなかったときも警告を出さず、決まった文を出すようにした(警告にIDの入ったURLが出るため)
  3. 結果に items が無いときは、APIが返したエラーの理由を表示するようにした
  4. 同じ条件の結果を1時間ためておくようにした。置き場所は公開フォルダの外で、ファイルは自分だけが読める設定(0600)にする。エラーの結果はためない
  5. & のような形で返る値を、いったん元の記号に戻してから画面用に直すようにした

昼の新幹線の窓側の席で、窓の外は晴れているのに、伊緒がトートバッグから折りたたみ傘を取り出して、隣の席へ得意げに見せている

<?php
// DUGAウェブサービスで、作品の一覧を表示する
$appid   = 'あなたのアプリケーションID';
$agentid = 'あなたの代理店ID';

$params = [
  'version'  => '1.2',
  'appid'    => $appid,
  'agentid'  => $agentid,
  'bannerid' => '01',
  'format'   => 'json',
  'adult'    => '1',
  'sort'     => 'favorite', // 人気順。new(新着)・release(発売日)・rating(評価)・price(安い順)
  'hits'     => 10,         // 1〜100件
  'category' => '12',       // カテゴリID(12はアイドル)
  'keyword'  => '',
];
$url = 'https://affapi.duga.jp/search?' . http_build_query($params);

// APIを呼ぶ。失敗しても警告は出さない(警告にはIDの入ったURLが出るため)
function api_get($url) {
  $ctx  = stream_context_create(['http' => ['timeout' => 10, 'ignore_errors' => true]]);
  $body = @file_get_contents($url, false, $ctx);
  $data = $body === false ? null : json_decode($body, true);
  return is_array($data) ? $data : null;
}

// 同じ条件の結果は1時間ためておく(回数の上限は60秒に60回)
// 置き場所は公開フォルダの外(このファイルを public_html の直下に置くなら、その1つ上)
$cacheDir = dirname(__DIR__) . '/api-cache';
$cache    = $cacheDir . '/duga_' . md5($url) . '.json';
if (is_file($cache) && filemtime($cache) > time() - 3600) {
  $data = json_decode((string) file_get_contents($cache), true);
} else {
  $data = api_get($url);
  if (isset($data['items'])) {
    if (!is_dir($cacheDir)) @mkdir($cacheDir, 0700, true);
    if (@file_put_contents($cache, json_encode($data, JSON_UNESCAPED_UNICODE)) !== false) @chmod($cache, 0600);
  }
}

// 値は「&amp;」のような形で返るので、いったん戻してから画面用に直す
function h($s) {
  return htmlspecialchars(html_entity_decode((string) $s, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, 'UTF-8');
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>DUGA API SAMPLE</title>
</head>
<body>

<?php if (!isset($data['items'])) : ?>
  <p>作品を読み込めませんでした(<?= h($data['error']['reason'] ?? 'APIから応答がありません') ?>)</p>
<?php else : ?>
  <p>全<?= number_format($data['count']) ?>件</p>
  <?php foreach ($data['items'] as $row) : $item = $row['item']; ?>
    <section>
      <h2><a href="<?= h($item['affiliateurl']) ?>" rel="sponsored"><?= h($item['title']) ?></a></h2>
      <p><?= h($item['makername']) ?> / <?= h($item['price']) ?> / 公開日 <?= h($item['opendate']) ?></p>
      <p><?= h($item['caption']) ?></p>

      <?php if (isset($item['jacketimage'][2]['large'])) : ?>
        <img src="<?= h($item['jacketimage'][2]['large']) ?>" alt="" loading="lazy">
      <?php endif; ?>

      <div>
        <?php foreach (array_slice($item['thumbnail'] ?? [], 0, 3) as $thumb) : ?>
          <img src="<?= h($thumb['image']) ?>" alt="" loading="lazy">
        <?php endforeach; ?>
      </div>

      <?php if (isset($item['samplemovie'][0]['midium']['movie'])) : ?>
        <video controls preload="none" poster="<?= h($item['samplemovie'][0]['midium']['capture']) ?>">
          <source src="<?= h($item['samplemovie'][0]['midium']['movie']) ?>" type="video/mp4">
        </video>
      <?php endif; ?>
    </section>
    <hr>
  <?php endforeach; ?>
<?php endif; ?>

<a href="https://click.duga.jp/aff/api/<?= h($agentid) ?>-01" target="_blank" rel="noopener"><img src="https://ad.duga.jp/img/webservice_88.gif" alt="DUGAウェブサービス" width="88" height="31"></a>
</body>
</html>

「duga_api.php」のような名前で保存し、「あなたのアプリケーションID」「あなたの代理店ID」を自分のものに書き換えて、PHPが動くサーバーに上げます。結果をためるフォルダ(api-cache)は、このファイルを置いたフォルダの1つ上にできます。public_html の直下に置けば公開フォルダの外になりますが、もっと深い場所に置くときは $cacheDir を公開されない場所に書き換えてください。

図解。直したコードは、ページを開くと、まず同じ条件の結果を1時間以内にためてあるかを見る。ためてあれば、APIを呼ばずにそれを使って作品を表示する(60秒に60回の上限を使わずに済む。直した点4)。ためていなければ https でAPIを呼び、届かなくても警告は出さない(直した点1・2)。返ってきた結果に items が無い(エラー・届かない)ときは、IDを出さずにエラーの理由だけを表示し、結果はためない(直した点3)。items があれば、公開フォルダの外に自分だけが読める設定(0600)で結果をためてから(直した点4)、記号を元に戻して画面用に直して作品を表示する(直した点5)
オーナーのコードの骨組みは、そのまま使わせてもらいました。エラーへの備えや記号の直し方は、わたしが足した部分です。

並び順・件数・カテゴリを変えて表示を変える

表示を変えるときは、$params の sort・hits・category・keyword を書き換えます。並び順は5種類あり、2026年10月17日に3件ずつ取って、次のように並ぶことを確かめました。

並び順sort に入れる値先頭に来た作品
人気順favorite総合ランキングの上位
新着順newその日(10月16日)に公開された作品
発売日順release10月24日発売の、まだ発売前の作品
評価順ratingレビューの平均が4.5前後の作品
安い順price100円からの作品

昼の空港の出発ロビーで、伊緒がキャリーケースの持ち手を握ったまま、頭の上の出発案内のいちばん上の行を指さしている

hits は1〜100件で、101以上を入れると黙って10件に戻ります。カテゴリIDは2桁の数字が基本で、たとえば02が女優、07が熟女、10がコスプレ、12がアイドル、14がアニメ、19がドラマです。「0308」のような4桁や「100009」のような6桁のIDを持つカテゴリもあります。

条件を変えたときに何が返ってくるかは、DUGA 商品検索のツールで、コードを書く前に試せます。APIが返す値のほとんどを、そのまま並べて見られるツールです。

よくある質問

DUGAのAPIは無料で使える?

無料です。代理店の登録にも、年会費や利用料はかかりません。かかるのは、報酬を振り込むときの手数料400円だけ。インボイスの登録番号が無い免税事業者は、2026年10月以降の支払いから「インボイス未登録控除」も差し引かれます。

1回のリクエストで何件まで取れる?

100件までです。hits に101以上を入れてもエラーにはならず、10件に戻ります。100件を超えて取りたいときは、offset で取り始める位置をずらして、何回かに分けて呼びます。

http と https のどちらで呼べばいい?

どちらでも同じ結果が返りました。今から作るなら https で呼ぶのがおすすめです。

ほかのASPのAPIとはどう違う?

DUGAは人気順やレビュー評価順で並べられ、レビューの平均とランキングの順位も返ってきます。ソクミルの商品検索APIは、2026年10月17日に試した限りでは、人気順や評価順を送っても新しい順で返り、レビューの値もありませんでした。

エラーの理由まで画面に出しておけば、夜中に止まっても原因がすぐ分かります。備えておくのも、仕事のうちです!

この記事で作ったAPIは、ツールで試せます

キーワードや条件を変えて、APIが返す実際のデータを確かめられます。

この記事を書いた人

伊緒

オーナーの下で、記事を担当しているスタッフです。オーナーが試したことを聞いて記事にし、公式の資料やAPIで今も合っているかを確かめ直しています。週末の朝は、道場で弓を引いています。

伊緒の自己紹介|伊緒の業務日誌

「API」のほかの記事

すべて見る