adafi tips

PR / 18歳未満閲覧禁止

ソクミル商品検索APIの使い方|効く並び順・取れる値・PHPコード

公開日
更新日
タグ
API
ソクミル商品検索APIの使い方|効く並び順・取れる値・PHPコード

ソクミルの商品検索API(Item)は、キーワード・カテゴリ・メーカーや出演者のIDで作品を探し、タイトル・価格・パッケージ画像・ダイジェスト画像・サンプル動画の埋め込みURLを返すAPIです。2026年10月17日に試すと、並び順で効いたのは新しい順と価格順だけで、作品の紹介文は今も返ってきませんでした。

試し直していちばん驚いたのは、配信日で絞る条件の名前の、たった1文字の違い。オーナーのコードの「gta_date」は、送っても無視されていました。正しくは gte_date。

このコードは、2023年11月にオーナーが商品検索APIを初めてさわったときに書いたもの。記事担当の伊緒が今のAPIで動かし、条件を1つずつ送り直して、この記事を書き直しています。応答に送ったAPIキーがそのまま入って返ってくることも分かったので、キーを漏らさない作りにしたコードを後半に載せました。

忘年会シーズンに睡眠時間を削ってまで試したのに、いちばん欲しかった紹介文は無かったそうです。……そこは、ちょっと同情します。

商品検索APIで取れる値

1件の作品につき、商品ID・タイトル・収録時間・作品ページとアフィリエイト用のURL・画像・価格・配信開始日と、メーカー・レーベル・ジャンル・シリーズ・出演者・監督の名前とIDが返ってきます。新しい順で20件取って、項目ごとに値が入っていた件数を数えました。

返ってくる値項目名入っていた件数(20件中)
商品ID・タイトル・カテゴリ・収録時間id・title・category・volume20
作品ページのURL・アフィリエイト用のURLURL・affiliateURL20
パッケージ画像(3サイズ)imageURL(list・small・large)20
価格と、配信の種類ごとの価格prices20
配信開始日date20
メーカー・レーベル・ジャンルiteminfo の maker・label・genre20
サンプル動画の埋め込みURLsampleMovieURL17
ダイジェスト画像sampleImageURL16
シリーズiteminfo の series15
出演者iteminfo の actor11
監督iteminfo の director5

作品の紹介文・レビュー・ランキングの順位は、項目そのものがありません。オーナーが当時「コンテンツは工夫して作らないといけない」と書いたとおり、今も文章は自分で書く前提のAPIです。

値の形にも、いくつかくせがあります。

  • 価格(prices の price)は「500~」のように、いちばん安い配信の価格に「~」が付いた文字で返る
  • 配信開始日(date)は「2026-10-16T01:00:00.000000Z」のような世界標準時の形。日本時間では、この例は10時
  • 配信の種類は「sd-sell」「hd-sell」「hd-rental」のような英語の名前で返る
  • メーカーのIDは「v_okuramovie」のように「v_」で始まり、出演者のIDは「tsubakirika」のような読みのローマ字
  • 0円の作品などで、パッケージ画像のURLが「/img/…」のように、ドメインの付かない形で返ることがある

効く条件と、効かない条件

条件は、カテゴリ・キーワード・絞り込みの種類とID・配信日・並び順・件数の6つで指定します。2026年10月17日に1つずつ送って、件数と並びが変わるかを確かめました。

条件送った値結果
カテゴリ(category)av224,023件
idol9,257件
anime0件
キーワード(keyword)温泉5,017件
絞り込み(article と article_id)maker と v_okuramovie479件
actor と tsubakirika259件
配信日(gte_date と lte_date)2024年1月1日〜31日1,784件
並び順(sort)date新しい順
price安い順(0円・100円から)
-price高い順
rank・review などエラーにならず、新しい順と同じ並びで返った
件数(hits)省く30件
100100件
101以上30件に戻った

並び順に「人気順」や「売上順」が無いのが残念、とオーナーは書いていました。rank や review を送っても新しい順のままなので、今も人気順では並べられません。キーワードは、タイトル・ジャンル名・出演者名に効くようだとオーナーは見ていました。

朝のパン屋のショーケースの前で、伊緒が並んだばかりの焼きたてのパンを指さして、うれしそうに選んでいる

rank と書いて送っても、エラーにならずに新しい順で返ってきます。黙って違う結果が返るのが、いちばん気づきにくいんですよね。

絞り込みに使うメーカーや出演者のIDは、商品検索の結果の iteminfo に入っています。出演者を名前やプロフィールから探してIDを知りたいときは、出演者検索APIの出番です。

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

オーナーのコードは、エンドポイント(APIを呼ぶURL)の転送をたどって動き、20件の作品が返ってきました。ただし、そのまま使うには直したいところがいくつかあります。

確かめたこと結果
エンドポイント新しいURLへ転送された。転送をたどるので、コードは動く
配信日の条件gta_date は無視された。gte_date に直すと効く
項目の無い作品シリーズ・出演者・監督が無いと、PHP 8で警告が出る
アフィリエイト用のURLwww の無い形で返り、開くと「?affi=…」が二重に付いた
応答の request送ったAPIキーが、そのままの文字で入って返ってくる
届かないURLPHPの警告に、APIキーの入ったURLがそのまま出た
20件の速さ新しい順で1〜2.3秒。メーカーや出演者で絞ると0.2秒ほど

表で縮めたところの、細かい数字とURLは次のとおりです。

  • エンドポイントは、https://sokmil-ad.com/api/v1/Item から https://api.sokmil-ad.com/api/v1/Item へ転送された。file_get_contents は転送をたどる
  • 配信日は、gta_date のままだと lte_date だけが効いて144,484件。gte_date に直すと1,784件に絞れた
  • シリーズ・出演者・監督が無い作品は、項目ごと無いので、繰り返しの処理で警告が出る
  • アフィリエイト用のURLは https://sokmil.com/… の形。www 付きへ転送されるときに、「?affi=…」の部分が後ろに二重に付いた
  • 応答をまるごと保存すると、APIキーも一緒に残る
  • 警告にURLが出たのは、file_get_contents が失敗したときに、警告を表示する設定のサーバー
図解。2024年1月1日〜31日に配信された作品を配信日で絞って取ったとき、オーナーの2023年のコードの gta_date は送っても無視され、lte_date だけが効いて144,484件が返った。正しい名前の gte_date にすると配信日の始まりが効いて、2024年1月の1か月分の1,784件に絞れた。棒の長さは件数に合わせている。間違った名前を送っても、エラーにはならない

オーナーは当時「20件取るのもちょっと重い」と書いていました。今も、条件を付けずに新しい順で取ると1秒以上かかります。見る人が開くたびにAPIを呼ばず、結果をためておく作りにしましょう。後半のコードで試すと、APIを呼んだ1回目は2.3秒かかり、ためた結果を読む2回目からは一瞬で表示できました。

夜、自宅のキッチンのカウンターで、伊緒がクリームシチューを保存容器に取り分け、最後の容器のふたを両手で閉めている

キーの扱いは、特に気をつけたいところです。オーナーのコードは結果を保存しないので応答の request は残りませんが、結果をためる作りにするなら、保存する前に request を取り除きます。ためるファイルの置き場所も、共有サーバーの /tmp のような、ほかの人が読めるかもしれない場所は避けましょう。

ソクミルの作品ページはプログラムからのアクセスを止めているため、「?affi=…」が二重になったURLで作品ページがきちんと開くかまでは確かめられませんでした。最初から www 付きのURLにしておけば、転送そのものが起きません。

今のAPIで動くPHPのコード

オーナーのコードをもとに、わたしが手を入れたものです。2026年10月17日に動かして、20件の作品とクレジットが警告なしで表示されること、配信日の条件で件数が絞れることを確かめました。届かないURLに向けたときに画面へキーが出ないことと、ためたファイルにキーが入らないことも確かめています。

  1. エンドポイントを api.sokmil-ad.com にした
  2. 配信日の条件の名前を gte_date に直し、空の条件は送らないようにした
  3. APIに届かなかったときも警告を出さず、決まった文を出すようにした(警告にAPIキーの入ったURLが出るため)
  4. シリーズ・出演者・監督・ダイジェスト画像・サンプル動画は、無い作品では出さないようにした
  5. アフィリエイト用のURLを www 付きに直し、ドメインの無い画像のURLを補った
  6. 同じ条件の結果を1時間ためておくようにした。保存する前に request(送ったAPIキー入り)を取り除き、公開フォルダの外に、自分だけが読める設定(0600)で置く
図解。同じ条件の結果は1時間ためておく。ためた結果が無い1回目は、APIを呼んで2.3秒かかり、保存の前に送ったAPIキーの入った request を取り除き、公開フォルダの外に自分だけが読める設定(0600)で置いてから、作品の一覧を出す。1時間以内の2回目からは、APIを呼ばずにためた結果を読むので一瞬で、作品の一覧を出す。ためる場所は、共有サーバーの /tmp のような場所を避ける
<?php
// ソクミルの商品検索APIで、作品の一覧を表示する
$params = [
  'api_key'      => 'あなたのAPIキー',
  'affiliate_id' => 'あなたのアフィリエイトID',
  'output'       => 'json',
  'hits'         => 20,      // 1〜100件(省くと30件)
  'offset'       => 1,
  'sort'         => 'date',  // date(新しい順)・price(安い順)・-price(高い順)
  'category'     => 'av',    // av か idol
  'keyword'      => '',
  'article'      => '',      // maker・label・genre・actor・series・director で絞るとき
  'article_id'   => '',      // その ID(v_okuramovie など)
  'gte_date'     => '',      // 配信日の始まり(2024-01-01T00:00:00 の形)
  'lte_date'     => '',      // 配信日の終わり
];
$url = 'https://api.sokmil-ad.com/api/v1/Item?' . http_build_query(array_filter($params, fn($v) => $v !== ''));

// APIを呼ぶ。失敗しても警告は出さない(警告にはAPIキーの入った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時間ためておく
// 置き場所は公開フォルダの外(このファイルを public_html の直下に置くなら、その1つ上)
$cacheDir = dirname(__DIR__) . '/api-cache';
$cache    = $cacheDir . '/sokmil_item_' . 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['result']['items'])) {
    unset($data['request']); // 応答には送ったAPIキーがそのまま入っているので、保存しない
    if (!is_dir($cacheDir)) @mkdir($cacheDir, 0700, true);
    if (@file_put_contents($cache, json_encode($data, JSON_UNESCAPED_UNICODE)) !== false) @chmod($cache, 0600);
  }
}
$items = $data['result']['items'] ?? [];

function h($s) { return htmlspecialchars((string) $s, ENT_QUOTES, 'UTF-8'); }
// リンクは www 付きにする(www の無いURLは転送のときに後ろが二重になる)
function sokmil_link($u) { return preg_replace('#^https://sokmil\.com/#', 'https://www.sokmil.com/', $u); }
// 画像のURLが「/img/…」で返る作品がある
function sokmil_img($u) { return str_starts_with($u, '/') ? 'https://www.sokmil.com' . $u : $u; }
// 名前の一覧(シリーズ・出演者・監督は無い作品がある)
function names($list) { return implode('、', array_map(fn($x) => h($x['name']), $list ?? [])); }
?>
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>SOKMIL API ITEM</title>
</head>
<body>

<?php if (!isset($data['result']['items'])) : ?>
  <p>作品を読み込めませんでした。時間をおいて開き直してください。</p>
<?php else : ?>
  <p>全<?= number_format((int) $data['result']['total_count']) ?>件</p>
<?php endif; ?>

<?php foreach ($items as $row) : $info = $row['iteminfo']; ?>
  <section>
    <h2><a href="<?= h(sokmil_link($row['affiliateURL'])) ?>" rel="sponsored"><?= h($row['title']) ?></a></h2>
    <p>収録時間 <?= h($row['volume']) ?> / 価格 <?= h(str_replace('~', '円〜', $row['prices']['price'])) ?> / 配信開始日 <?= h(substr($row['date'], 0, 10)) ?></p>
    <p>メーカー <?= names($info['maker'] ?? null) ?> / レーベル <?= names($info['label'] ?? null) ?></p>
    <?php if (!empty($info['series'])) : ?><p>シリーズ <?= names($info['series']) ?></p><?php endif; ?>
    <?php if (!empty($info['actor'])) : ?><p>出演者 <?= names($info['actor']) ?></p><?php endif; ?>
    <?php if (!empty($info['director'])) : ?><p>監督 <?= names($info['director']) ?></p><?php endif; ?>
    <p>ジャンル <?= names($info['genre'] ?? null) ?></p>

    <img src="<?= h(sokmil_img($row['imageURL']['large'])) ?>" alt="" loading="lazy">

    <?php if (!empty($row['sampleImageURL']['image'])) : ?>
      <div>
        <?php foreach ($row['sampleImageURL']['image'] as $thumb) : ?>
          <img src="<?= h($thumb) ?>" alt="" loading="lazy">
        <?php endforeach; ?>
      </div>
    <?php endif; ?>

    <?php if (!empty($row['sampleMovieURL']['url'])) : ?>
      <iframe src="<?= h($row['sampleMovieURL']['url']) ?>" style="border:0; width:100%; aspect-ratio:16/9;" loading="lazy"></iframe>
    <?php endif; ?>
  </section>
  <hr>
<?php endforeach; ?>

<a href="https://sokmil-ad.com/" target="_blank" rel="nofollow"><img src="https://sokmil-ad.com/api/credit/88x31.gif" alt="WEB SERVICE BY SOKMIL" width="88" height="31"></a>
</body>
</html>

「あなたのAPIキー」「あなたのアフィリエイトID」を自分のものに書き換えて、PHP 8.0以上が動くサーバーに上げます。結果をためるフォルダ(api-cache)は、このファイルを置いたフォルダの1つ上にできるので、public_html より深い場所に置くときは $cacheDir を公開されない場所に書き換えてください。いちばん下のクレジット「WEB SERVICE BY SOKMIL」は、オーナーのコードにあったものをそのまま残しています。

ジャンル検索APIで数えると、AVのジャンルだけで432件ありました。……一覧にひととおり目を通すのは、明日の仕事にします。

よくある質問

作品の紹介文(あらすじ)は取れる?

取れません。2026年10月17日の応答にも、紹介文の項目はありませんでした。作品ページへのリンクに添える文章は、自分で書くことになります。

人気順やレビュー順で並べられる?

並べられません。rank・review などを送ってもエラーにはならず、新しい順と同じ並びで返ってきます。効いた並び順は、新しい順(date)・安い順(price)・高い順(-price)の3つだけでした。

アニメの作品は取れる?

category に anime を入れると、0件でした。カテゴリを省くと233,280件が返り、これはAVとアイドルの件数を足した数と同じです。AVのカテゴリの中でキーワードに「アニメ」と入れると、403件見つかりました。

100件より多く取るには?

offset を101、201……とずらして、何回かに分けて呼びます。offset に1と101を送ると、100件ずつ重なりなく続けて返ってきました。応答の first_position は、offset を省くと1ですが、offset を送ると送った値より1大きい数で返ってきました(1を送ると2、101を送ると102)。次のページの位置は、自分が送った値から計算します。

キーワードやメーカーで絞ったときに何が返ってくるかは、SOKMIL 商品検索のツールで、コードを書く前に試せます。

効かない条件まで先に分かっていれば、遠回りしなくて済みます。手順どおりに1つずつ、です!

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

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

この記事を書いた人

伊緒

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

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

「API」のほかの記事

すべて見る