短縮リンクの運用を自動化する — toui.io API 開発者ガイド
マーケティング担当が、共有スプレッドシートに商品URLを50本流し込んできました。件名は「今週末セール。金曜までに、これ全部の計測できるリンクをお願いします」。
スプレッドシートを開きます。50行。1行ごとに、わかりやすいタイトルとソーシャルプレビューのメタデータを付けた短縮リンクが要ります。FacebookやXで共有したときに、リンクの見た目が整っているようにするためです。toui.io のダッシュボードを開いて、URLを1本ずつ貼って、OGの項目を埋めて、短縮リンクをコピーして戻して……それを、あと49回。
あるいは、30秒で終わるスクリプトを書くこともできます。
この記事で書くのは、まさにそれです。私が toui.io をつくった理由のひとつが、この場面に何度も出くわしたことでした。だからAPIは、この作業を中心に設計してあります。読み終わるころには、短縮リンクをつくり、それを確認し、キャンペーンの成果を取り出すところまでが動いています。ブラウザは不要です。
始める前に
用意するものは3つです。
-
toui.io のアカウント。 Freeプランでも REST API を使えます(60 リクエスト/分、5,000 リクエスト/月、500 リクエスト/日)。連携をひととおりつくるには十分です。バーストや月間の上限がもっと必要になったら、Pro か Business にアップグレードしてください。料金プランはこちら。
-
APIキー。 toui.io のダッシュボードにログインし、「API」のページでキーを作成します。表示されるのは一度きりなので、コピーしてプロジェクト直下の
.envファイルに保存してください。
# .env
TOUI_API_KEY=toui_your_key_here
.env が .gitignore に入っていることを確認してください。APIキーは、絶対にバージョン管理にコミットしないでください。
- Node.js 18以上(
fetchが組み込みで使えます)と、環境変数を読み込む dotenv。
npm install dotenv
Node.js 20.6以上なら、dotenv を省いて組み込みの
--env-fileフラグを使えます。node --env-file=.env create-links.js
この記事のコードは、どれも環境変数からキーを読みます。どのスクリプトも、冒頭はこうなります。
import "dotenv/config";
const API_KEY = process.env.TOUI_API_KEY;
if (!API_KEY) {
throw new Error("Missing TOUI_API_KEY in .env file");
}
このチェックが最初に走ります。誰かがリポジトリをクローンして .env をつくり忘れたとき、意味のわからない401ではなく、はっきりしたエラーを受け取れます。
ステップ1 — 短縮リンクをつくる
エンドポイントは POST https://toui.io/api/v1/shorten です。リンク先のURLと、任意のメタデータをJSONで送ると、短縮リンクが返ってきます。
いちばん短い呼び出しは、これです。
const response = await fetch("https://toui.io/api/v1/shorten", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.TOUI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://shop.example.com/products/wireless-earbuds-pro",
title: "Wireless Earbuds Pro - Flash Sale",
}),
});
if (!response.ok) {
const err = await response.json();
throw new Error(`API error ${response.status}: ${err.error}`);
}
const data = await response.json();
console.log(data);
// {
// short_url: "https://toui.io/xK3mQp",
// short_code: "xK3mQp",
// target_url: "https://shop.example.com/products/wireless-earbuds-pro",
// created_at: "2026-04-10T08:30:00.000Z"
// }
これが基本の形です。URLをPOSTし、response.ok を確認し、結果をパースする。以下の例も、すべて同じ形をしています。
ソーシャルプレビューのメタデータを付ける
短縮リンクがFacebookやX、Slackで共有されると、各プラットフォームはプレビューカードを描くためにOpen Graphのタグを取りに行きます。toui.io では、この内容を作成時に指定できます。
const result = await createLink({
url: "https://shop.example.com/products/wireless-earbuds-pro",
title: "Wireless Earbuds Pro - Flash Sale",
og_title: "60% Off Wireless Earbuds Pro -- This Weekend Only",
og_description: "Our top-rated earbuds at the lowest price ever. 48 hours only.",
});
og_title と og_description は任意です。省いた場合、各プラットフォームはリンク先ページのメタデータをそのまま使います。とはいえ、1クリックでも多く取りたいセールでは、2行追加してでもプレビューカードを自分で指定する価値があります。
画像を用意してあるなら og_image_url も渡せます。公開されている https:// の画像URLなら、何でも受け付けます。
短縮コードを自分で決める
toui.io は、既定ではランダムな6文字のコードを生成します。ただ、読める形にしたいとき——ポッドキャストで読み上げるリンクや、パッケージに印刷するリンクなど——には、コードを指定できます。
{
url: "https://shop.example.com/flash-sale",
title: "Weekend Flash Sale",
custom_code: "sale426"
}
// 結果:https://toui.io/sale426
カスタム短縮コードは英数字4〜16文字です。すでに使われているコードを指定すると、APIは400を返します。これは有料プランの機能で、Freeプランのアカウントでは自動生成のコードのみが使えます。
50件の商品をループで処理する
ここまでをまとめます。商品リストを読み込んで、それぞれに計測できる短縮リンクをつくる、完成版のスクリプトです。
import "dotenv/config";
const API_KEY = process.env.TOUI_API_KEY;
if (!API_KEY) {
throw new Error("Missing TOUI_API_KEY in .env file");
}
const API_BASE = "https://toui.io/api/v1";
// マーケティング担当から届いた商品リスト
const products = [
{
name: "Wireless Earbuds Pro",
url: "https://shop.example.com/products/wireless-earbuds-pro",
discount: "60%",
},
{
name: "USB-C Hub 7-in-1",
url: "https://shop.example.com/products/usb-c-hub-7in1",
discount: "45%",
},
// ……残り48件
];
async function createLink(body) {
const response = await fetch(`${API_BASE}/shorten`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!response.ok) {
const err = await response.json();
throw new Error(`Failed to shorten ${body.url}: ${err.error}`);
}
return response.json();
}
async function main() {
const results = [];
for (const product of products) {
const result = await createLink({
url: product.url,
title: `${product.name} - Flash Sale`,
og_title: `${product.discount} Off ${product.name} -- This Weekend Only`,
og_description: `Flash sale pricing on ${product.name}. Limited time offer.`,
});
results.push({
product: product.name,
short_url: result.short_url,
code: result.short_code,
});
console.log(`Created: ${result.short_url} -> ${product.name}`);
}
console.log(`\nDone. Created ${results.length} short links.`);
return results;
}
main().catch(console.error);
実行すると、ターミナルに50本の短縮リンクが並びます。どれも計測でき、ソーシャルプレビューのメタデータが付いていて、メールの配信、広告のクリエイティブ、SNSの投稿にそのまま入れられます。
ステップ2 — つくったリンクを確認する
マーケティング担当にリンクを渡す前に、ざっと点検しておくと安心です。GET /api/v1/urls/{code} は、その短縮リンクについて toui.io が持っている情報をすべて返します。
async function getLink(code) {
const response = await fetch(`${API_BASE}/urls/${code}`, {
headers: {
"Authorization": `Bearer ${API_KEY}`,
},
});
if (!response.ok) {
const err = await response.json();
throw new Error(`Lookup failed for ${code}: ${err.error}`);
}
return response.json();
}
const link = await getLink("xK3mQp");
console.log(link);
// {
// short_code: "xK3mQp",
// target_url: "https://shop.example.com/products/wireless-earbuds-pro",
// title: "Wireless Earbuds Pro - Flash Sale",
// click_count: 0,
// is_active: 1,
// og_title: "60% Off Wireless Earbuds Pro -- This Weekend Only",
// og_description: "Our top-rated earbuds at the lowest price ever. 48 hours only.",
// og_image_url: null,
// created_at: "2026-04-10T08:30:00.000Z"
// }
抜き取り検査に便利です。target_url が正しいか、OGの項目が入っているか、is_active が 1 かを確認できます。50本すべてに対して走らせて、スプレッドシートを返す前の検証工程にしてもいいでしょう。
ステップ3 — キャンペーンの成果を見る
セールが終わりました。何が効いたのかを見る番です。GET /api/v1/urls/{code}/stats は、日別の推移と訪問者の内訳を含むクリックデータを返します。
async function getStats(code, days = 7) {
const response = await fetch(`${API_BASE}/urls/${code}/stats?days=${days}`, {
headers: {
"Authorization": `Bearer ${API_KEY}`,
},
});
if (!response.ok) {
const err = await response.json();
throw new Error(`Stats failed for ${code}: ${err.error}`);
}
return response.json();
}
const stats = await getStats("xK3mQp", 7);
console.log(stats);
// {
// short_code: "xK3mQp",
// target_url: "https://shop.example.com/products/wireless-earbuds-pro",
// total_clicks: 1284,
// daily: [
// { date: "2026-04-10", clicks: 412, unique_visitors: 389 },
// { date: "2026-04-09", clicks: 507, unique_visitors: 461 },
// { date: "2026-04-08", clicks: 365, unique_visitors: 330 },
// ...
// ],
// countries: [
// { country: "TW", clicks: 623 },
// { country: "JP", clicks: 287 },
// { country: "US", clicks: 194 },
// ...
// ],
// referers: [
// { referer: "facebook.com", clicks: 512 },
// { referer: "line.me", clicks: 401 },
// ...
// ],
// devices: [
// { device_type: "mobile", browser: "Chrome Mobile", clicks: 743 },
// { device_type: "desktop", browser: "Chrome", clicks: 412 },
// ...
// ],
// limited: false
// }
limited について補足します。国・参照元・デバイスの詳しい内訳は、Pro か Business のプランで全件が見られます。Freeプランでは limited: true が付いた短い形で返り、各項目の上位3件までが含まれます。連携が正しく動いているかを確かめるには十分です。全件を見るには、アップグレードしてください。
コンソールにレポートを出す
キャンペーンのリンクをまとめて集計し、要約を表示する短いスクリプトです。
async function campaignReport(codes) {
console.log("Product Link Report");
console.log("=".repeat(60));
let totalClicks = 0;
for (const code of codes) {
const stats = await getStats(code, 7);
totalClicks += stats.total_clicks;
const topCountry = stats.countries[0];
const topReferer = stats.referers[0];
console.log(`\n${stats.short_code} -> ${stats.target_url}`);
console.log(` Total clicks: ${stats.total_clicks}`);
if (topCountry) {
console.log(` Top country: ${topCountry.country} (${topCountry.clicks})`);
}
if (topReferer) {
console.log(` Top referrer: ${topReferer.referer} (${topReferer.clicks})`);
}
}
console.log(`\n${"=".repeat(60)}`);
console.log(`Campaign total: ${totalClicks} clicks across ${codes.length} links`);
}
// ステップ1で受け取った短縮コードを渡す
const codes = ["xK3mQp", "bR7nWz", /* ... */];
campaignReport(codes).catch(console.error);
キャンペーン後の分析はこれで全部です。ダッシュボードをクリックして回る必要も、CSVを書き出して整える必要もありません。チームがすでに使っているレポートツールに、そのまま流し込めるデータが手に入ります。
運用のこつ
レート制限
toui.io は、プランごとに1分あたりのバースト上限を設けています。
| プラン | バースト上限 | 月間のリクエスト数 |
|---|---|---|
| Free | 60 リクエスト/分 | 5,000 リクエスト/月 |
| Pro | 200 リクエスト/分 | 500,000 リクエスト/月 |
| Business | 600 リクエスト/分 | 2,000,000 リクエスト/月 |
商品50件なら、どちらの上限にも届きません。ただ、何千本ものリンクを発行する仕組み——たとえば取扱量の多いショップで、注文ごとに追跡用URLを出すようなもの——をつくるなら、リクエストの間隔を調整したくなるはずです。
429(レート制限)に対処する
上限に達すると、APIは429を返します。単純なバックオフで、きれいに処理できます。
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status === 429 && attempt < maxRetries) {
const body = await response.json();
const delay = (body.retry_after || Math.pow(2, attempt)) * 1000;
console.log(`Rate limited. Retrying in ${delay / 1000}s...`);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
return response;
}
}
429のレスポンスボディには retry_after(秒)が入っていて、どれだけ待てばよいかがそのままわかります。成功したレスポンスにも X-RateLimit-Remaining ヘッダーが付くので、必要なら先回りして間隔を調整できます。
エラーコード早見表
| ステータス | 意味 | よくある原因 |
|---|---|---|
| 400 | リクエストが不正 | url がない、カスタムコードが不正、JSONの形式が誤っている |
| 401 | 認証されていない | APIキーがない、または無効 |
| 403 | 許可されていない | Freeプランでカスタムコードを指定した、またはURLが Safe Browsing で危険と判定された |
| 404 | 見つからない | 短縮コードが存在しない、または別のチームのもの |
| 429 | レート制限 | 1分あたりのリクエストが多すぎる、または月間の上限を超えた |
パースの前に、必ず response.ok を確認してください。エラーのレスポンスボディには、人が読めるメッセージが error に必ず入っています。それをログに出しておけば、原因追跡はぐっと楽になります。
いま、つくり終えたもの
100行に満たないJavaScriptで、次のことをするキャンペーン用のしくみができました。
- ソーシャルプレビューカード付きの、計測できる短縮リンクを50本つくる
- 1本ずつ、設定が正しいかを確認する
- キャンペーン後に、国・参照元・デバイスの内訳を含む分析データを取り出す
Node.js のほかに依存はありません。ダッシュボードのクリックもありません。node create-links.js を実行すれば、リンクが配れる状態になっています。
同じ形は、計測できるリンクをまとめて必要とするもの全般に使えます。注文確認、イベントの招待、メールマガジンの配信、アフィリエイトの管理。
APIはこれで全部です。エンドポイントは3つ、インストールするSDKもなく、fetch だけ。toui.io を個人のプロジェクトとしてつくったのは、Bitly のようなツールが備えるもののうち、私に必要だったのは5%ほどで、そのために月35米ドルを払うのは割に合わないと思ったからです。
まずは試してみたい方のためにFreeプランがあります。APIはどのプランでも使えます。週に数本つくるだけなら、Freeプランで始めるのに十分です。バースト上限や月間のリクエスト数がもっと必要になったら、Pro か Business にアップグレードしてください。