adafi tips

PR / 18歳未満閲覧禁止

DMM・FANZA商品検索APIの使い方|PHPコードと返る項目

公開日
更新日
タグ
API
DMM・FANZA商品検索APIの使い方|PHPコードと返る項目

DMM・FANZAの商品検索API(ItemList)は、サイト・サービス・フロアを指定して呼ぶと、作品名・商品ID・価格・発売日・女優やジャンルの名前とIDを、1回に最大100件返します。2023年にオーナーが書いたPHPのコードは、2026年10月17日に動かしても、そのまま作品の一覧を出せました。

記事担当の伊緒です。2023年11月にオーナーが商品検索APIを初めて触ったときの記録を、今の公式ドキュメントと突き合わせ、同じ呼び出しを今のAPIで実行して書き直しています。

動いたとはいえ、当時のコードには直したい点が3つありました。中でも気をつけたいのは、エラーのときにPHPの警告が出て、API ID入りのURLが画面に出てしまうこと。直したコードは、この記事の後半に載せています。

「とりあえず動いた」で止めるのが、オーナーの悪いくせです。エラーのときの動きまで試してから、動いたって言ってほしいです……。

商品検索APIで返ってくる項目

FANZAの動画(ビデオ)を人気順に100件取ると、作品名・商品ID・価格・発売日・画像・ジャンル・メーカー・レーベルは、100件すべてに入っていました。女優・シリーズ・レビューなどは、作品によって入らないことがあります。2026年10月17日に、site=FANZA&service=digital&floor=videoa&hits=100&sort=rank で呼んだ結果です。

中身項目名100件のうち入っていた件数
作品名・商品ID・品番title・content_id・product_id100件
商品ページとアフィリエイトのURLURL・affiliateURL100件
発売日(配信開始日)・収録時間date・volume(収録時間は分)100件
価格(配信の種類ごとも)prices100件
パッケージ画像(3サイズ)imageURL(list・small・large)100件
ジャンル・メーカー・レーベルiteminfo.genre・maker・label(名前とID)100件
サンプル画像(小・大)sampleImageURL99件
女優(名前・ID・読み仮名)iteminfo.actress89件
サンプル動画(4サイズ)sampleMovieURL(PC・スマホに対応しているかも入る)87件
レビューの件数と平均点review76件
監督(名前・ID・読み仮名)iteminfo.director73件
シリーズ(名前とID)iteminfo.series52件
セールなどのキャンペーンcampaign(名前と期間)47件

女優と監督には、公式ドキュメントの一覧に無い読み仮名(ruby)も付いていました。女優の名前を50音順に並べたいときに使えます。

コードを書くときに引っかかりやすい点も、3つ見つかりました。

  • 価格は文字列:prices.price は「2680~」のように、最安値の後ろに「~」が付いた文字列で返る。数として計算するときは、数字だけを取り出す
  • 全体件数は50000で止まる:FANZAの動画のように作品が多い売り場では、total_count が50000になる。検索開始位置(offset)の上限も50000なので、それより先の作品は、条件を絞らないと取れない
  • 売り場で項目が変わる:通販はJANコード・在庫状況(stock)・パンくずリスト(directory)、電子書籍は立ち読みのURL(tachiyomi)と巻数が返る。DMM TVの作品には、画像のURLとジャンルが入らない

立ち読みのURLは、公式ドキュメントでは affilaiteURL と書かれていますが、実際の応答の項目名は affiliateURL でした。ドキュメントの綴りのままコードを書くと、値が取れません。

当時のコードが出していたメーカー品番(maker_product)は、今の公式ドキュメントの一覧に載っていませんでした。カラー・サイズ・ISBNは一覧にあるものの、フロアを変えて12回呼んだ、のべ860件のどれにも入っていません。

返ってきた値は、FANZA・DMM 商品検索のツールで、キーワードや並び順を変えながら確かめられます。画面のいちばん下に、APIの元の応答(JSON)も出るので、項目名を写すときに便利です。

指定できる条件と、その上限

必ず指定するのは、API ID・アフィリエイトID・サイト(site)の3つです。ほかの条件は省いても動き、件数は初期値の20件で返ります。

パラメーター中身決まり
siteFANZA(アダルト)か DMM.com(一般)か必須。省くと400のエラー
service・floor売り場(サービスとフロアのコード)フロアAPIで調べた組で渡す
hits1回に取る件数初期値20・最大100
offset何件目から取るか初期値1・最大50000
sort並び順rank・date・review・price・-price・match
keywordキーワードUTF-8で渡す
cid商品ID(content_id)1作品だけ取るとき
article・article_id女優・作者・ジャンル・シリーズ・メーカーで絞るIDは各検索APIで調べる
gte_date・lte_date発売日の範囲時刻まで書く(2026-10-01T00:00:00)
mono_stock在庫で絞る通販のときだけ

並び順の値は、rank が人気順、date が発売日順、review が評価順、price が高い順、-price が安い順、match がマッチング順を表します。

実際に呼んで分かった決まりは、次のとおりです。

  • hits=101 にすると、400のエラーになり、「hits は100以下にする」という意味の英語の文が返る。offset=50001 も同じく400になる
  • 発売日を gte_date=2026-10-01 のように日付だけで渡すと、400のエラーになる。2026-10-01T00:00:00 と時刻まで書くと通り、10月1日〜16日に発売されたFANZAの動画は1,545件だった
  • 絞り込みは article[0]=genre&article_id[0]=6548&article[1]=maker&article_id[1]=3152 のように配列にすると、両方に当てはまる作品だけになった
  • 発売日順(sort=date)にすると、発売前の予約作品が先に並ぶ。コスプレのジャンルで試すと、1件目は2026年11月10日発売の作品だった

絞り込みに使うジャンルのIDは、ジャンル検索APIで調べられます。同じ名前でIDが2つ以上あるジャンルがあり、作品が0件のIDも混ざっていたので、IDの選び方はそちらの記事に書きました。

女優のIDは、女優検索APIで名前から調べます。

サイト・サービス・フロアの組み合わせで気をつけること

サービスとフロアは、フロアAPIが返す組のまま渡します。組が合わないと、エラーで止まる場合と、エラーにならずに0件で返る場合があり、後者は気づきにくいので要注意です。

  • floor=videoa だけを渡して service を省く → 400のエラー(floor が Invalid)
  • service=digital&floor=dvd のように、サービスに無いフロアを渡す → 400のエラー
  • site=DMM.com&service=digital&floor=videoa → エラーにならず、0件で返る
図解。サービスとフロアの組が合わないときの返り方。service を省いて floor=videoa だけを送ると400のエラー(floor が Invalid)、service=digital&floor=dvd のようにサービスに無いフロアを渡すと400のエラー、FANZAの組を DMM.com のサイトで呼ぶ site=DMM.com&service=digital&floor=videoa はエラーにならずに0件で返り、気づきにくい。0件のときは、まずサイトとサービスの組を疑う

3つ目は、FANZAの組を DMM.com のサイトで呼んだ形です。「APIは正常に答えたのに作品が無い」と見えるので、0件のときは、まずサイトとサービスの組を疑ってください。フロアのコードは dvd・anime・comic のように、別のサービスにも同じものがあるので、コードだけで売り場は決まりません。

エラーで止まってくれるほうが、まだ親切です。0件のまま気づかないのが、いちばん怖いんですから。

夕方のスーパーの棚の通路で、品物の並んだ棚の中に1段だけ空っぽの段を見つけた伊緒が、棚板に手をのせて目を丸くしている

PHPのコード:今のAPIで動かして直したもの

オーナーが2023年に書いたコードは、返ってくる項目を全部画面に出す確認用で、300行近くあります。2026年10月17日にそのまま動かすと、10件の作品が警告なしで表示されました。それでも、自分のサイトに使うなら、次の3つは直したほうが安心です。

  1. エラーのときに、API IDが画面に出るおそれがある。件数を101にして試すと、file_get_contents が「400 Bad Request」の警告を出し、その文にAPI ID入りのURLがそのまま入っていた。PHPの設定で警告を画面に出すサーバーだと、閲覧者に見えてしまう。利用規約でも、API IDは他人に知られないよう管理することになっている
  2. エラーの理由が分からない。400のときは応答の本文に理由が入っているのに、当時のコードでは受け取れず、「foreach() argument must be of type array|object」(配列でないものを foreach に渡した)という警告だけが出る
  3. 作品名などをエスケープしていない。作品名には「&」や「<」が入ることがあるので、htmlspecialchars を通してから出す
図解。件数を101にして呼ぶと400のエラーになる。2023年のコードでは、閲覧者の画面に、file_get_contents の警告としてAPI ID入りのURLが出て、そのあと「foreach() argument must be of type array

メーカー品番(maker_product)のように、今のドキュメントに無い項目も外しました。直したコードがこちらです。

<?php
// DMM・FANZA 商品検索API(ItemList)で、作品の一覧を表示する
const API_ID = 'あなたのAPI ID';
const AFFILIATE_ID = 'あなたのアフィリエイトID'; // 末尾が990〜999のもの

// API を呼んで result を返す。エラーのときは、APIが返した理由を出して止める
function dmm_api(string $name, array $params): array
{
    $query = http_build_query(['api_id' => API_ID, 'affiliate_id' => AFFILIATE_ID, 'output' => 'json'] + $params);
    // ignore_errors を付けると、400 のときもエラーの理由(本文)を受け取れる
    $context = stream_context_create(['http' => ['ignore_errors' => true, 'timeout' => 10]]);
    // 先頭の @ は、つながらなかったときの警告に API ID 入りの URL を出さないため
    $body = @file_get_contents('https://api.dmm.com/affiliate/v3/' . $name . '?' . $query, false, $context);
    $result = json_decode((string) $body, true)['result'] ?? null;
    if (!is_array($result) || (int) ($result['status'] ?? 200) !== 200) {
        exit('APIのエラー: ' . htmlspecialchars(json_encode($result ?? '応答がありません', JSON_UNESCAPED_UNICODE)));
    }
    return $result;
}

function h($value): string
{
    return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}

// iteminfo の女優・ジャンルなどを「名前(ID)」で並べる
function names(array $item, string $key): string
{
    $list = array_map(fn($x) => h($x['name']) . '(' . h($x['id']) . ')', $item['iteminfo'][$key] ?? []);
    return implode('、', $list);
}

$result = dmm_api('ItemList', [
    'site' => 'FANZA',
    'service' => 'digital', // サービスとフロアは、フロアAPIで調べた組で渡す
    'floor' => 'videoa',
    'hits' => 10,           // 1回に取れる件数。最大100
    'offset' => 1,          // 何件目から取るか。最大50000
    'sort' => 'rank',       // rank・date・review・price・-price・match
    'keyword' => '',
]);
?>
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>DMM 商品検索API</title>
</head>
<body>
  <p><?= h($result['total_count']) ?>件のうち、<?= h($result['first_position']) ?>件目から<?= h($result['result_count']) ?>件</p>

  <?php foreach ($result['items'] as $item) : ?>
    <section>
      <h2><a href="<?= h($item['affiliateURL']) ?>" target="_blank" rel="sponsored noopener"><?= h($item['title']) ?></a></h2>
      <?php if (!empty($item['imageURL']['list'])) : ?>
        <img src="<?= h($item['imageURL']['list']) ?>" alt="<?= h($item['title']) ?>" loading="lazy">
      <?php endif; ?>
      <ul>
        <li>商品ID:<?= h($item['content_id']) ?>(<?= h($item['floor_name']) ?>)</li>
        <li>発売日:<?= h($item['date'] ?? '') ?></li>
        <li>価格:<?= h($item['prices']['price'] ?? '') ?></li>
        <?php if (!empty($item['review'])) : ?>
          <li>レビュー:<?= h($item['review']['average']) ?>点(<?= h($item['review']['count']) ?>件)</li>
        <?php endif; ?>
        <li>女優:<?= names($item, 'actress') ?></li>
        <li>ジャンル:<?= names($item, 'genre') ?></li>
        <li>メーカー:<?= names($item, 'maker') ?> / レーベル:<?= names($item, 'label') ?></li>
        <li>シリーズ:<?= names($item, 'series') ?></li>
      </ul>
      <?php if (!empty($item['sampleMovieURL']['size_560_360'])) : ?>
        <iframe src="<?= h($item['sampleMovieURL']['size_560_360']) ?>" width="560" height="360" style="max-width:100%;border:0;" allowfullscreen loading="lazy"></iframe>
      <?php endif; ?>
    </section>
  <?php endforeach; ?>

  <a href="https://affiliate.dmm.com/api/"><img src="https://p.dmm.co.jp/p/affiliate/web_service/r18_135_17.gif" width="135" height="17" alt="WEB SERVICE BY FANZA" /></a>
</body>
</html>

API_ID と AFFILIATE_ID を自分の値に書き換えて使います。アフィリエイトIDは、末尾が990〜999のものしか受け付けません(それ以外はエラー)。このコードを2026年10月17日に動かすと、10件が警告なしで出て、件数を100にしても0.5秒ほどで返りました。件数を101にしたときは、画面にAPIが返したエラーの理由(hits は100以下にする、という英語の文)だけが出て、API IDは出ていません。

ページのいちばん下にあるのは、APIを使ったサイトに求められているクレジットの表示です。FANZAのデータを使うときはFANZAの、DMM.comのデータを使うときはDMM.comのものを、公式のHTMLのまま貼ります。HTMLや画像を書き換えると、APIの利用を止められることがあると書かれています。

100件の作品名が並ぶ画面で、項目を1つずつ数えました。……顔が熱かったのは、ノートパソコンの熱のせいです。

午前のコワーキングスペースの窓ぎわの席で、伊緒がノートPCの一覧を指で数えながら、赤くなった顔を片手であおいでいる

2023年のオーナーの感想を、今のAPIで見直すと

オーナーが当時の記事に書いた感想は、今もおおむね当てはまっていました。

項目オーナーが2023年に書いたこと2026年10月17日に確かめた結果
並び順人気順・新着順・レビュー評価順・マッチング順が充実していて重宝しそう今も同じ。価格の高い順・安い順を加えた6種類
絞り込みフロア・女優・作者・ジャンル・シリーズ・メーカーのAPIも触る必要がある今も6つとも提供されている。絞り込みの article に使えるのは女優・作者・ジャンル・シリーズ・メーカーのID
項目の多さおもちゃや漫画も扱うので、立ち読みや色・サイズの項目がほかのASPより多い立ち読みは電子書籍で返る。色・サイズは一覧にあるが、今回の860件には無かった
速さ100件を頼んでも、比較的早く返ってくる手元から100件を頼んで、0.5秒ほど

APIを使い始めるまでの経緯も、当時の記事にあります。DMMアフィリエイトのサイトの審査をずっと待っていて、管理画面を見に行くと、いつの間にか通っていたそうです。3記事ほどのサイトで申し込んで、すんなり通ったとオーナーは書いていました。今の公式のご利用ガイドでは、DMMアフィリエイトに登録したうえで、Webサービスの利用登録をするとAPI IDが発行される、という流れになっています。

よくある質問

1回に何件まで取れる?

1回に100件までです。101件以上を頼むと、400のエラーが返ります。続きは offset を101、201…とずらして取りますが、offset の上限が50000なので、それより先は、ジャンルや発売日で条件を絞って取ってください。

エラーのときは、何が返ってくる?

HTTPの状態が400になり、本文の result に status: 400・message: BAD REQUEST と、項目ごとの理由(errors)が入ります。件数が多すぎるときなら、errors に「The hits field must contain a number less than or equal to 100.」(hits は100以下にする)という英語の文が入る、という具合です。同じ本文の request には、送ったAPI IDもそのまま入っています。エラーを画面に出すときは、result の部分だけにしてください。

DMM TVの作品も取れる?

取れます。site=DMM.com&service=dmmtv&floor=dmmtv_video で呼ぶと作品名と価格は返りますが、画像のURLとジャンルは入りませんでした。DMMのヘルプでも、DMM TVは画像のURLとジャンルの情報を取れないと案内されています。

アフィリエイトIDは、普段のリンクと同じものでいい?

APIには、末尾が990〜999のアフィリエイトIDを使います。それ以外のIDではエラーになると、ご利用ガイドに書かれているためです。

アフィリエイトIDの末尾は、APIを使う前にもう一度確かめておきましょう。わたしは、紙の手帳にも書いてあります。

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

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

この記事を書いた人

伊緒

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

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

「API」のほかの記事

すべて見る