প্রকৃত ছবি দিয়ে মাসে ১০,০০০ পেজ ইলাস্ট্রেট করুন — মাত্র ৫০০ API কলে
প্রতিটি ছবির জন্য নয়, বরং প্রতিটি টপিক ক্লাস্টারের জন্য একবার সার্চ করুন, আর ৪০,০০০ API কল হয়ে যায় ৫০০টি। পুল-অ্যান্ড-অ্যাসাইন প্যাটার্ন, রেট লিমিট সামলাতে সক্ষম ওয়ার্কার, এবং ছবি দিয়ে কী ঠিক করা যায় না তার সৎ আলোচনা।
কনটেন্ট-ইলাস্ট্রেশন সমস্যার এমন একটি সংস্করণ আছে যা ভালো রুচি দিয়ে সমাধান করা যায় না: আপনি একটা ছবি বাছাই করছেন না, আপনি মাসে ৮০০টি টপিক ক্লাস্টার, ৯টি লোকেল এবং ১৪টি ক্লায়েন্ট সাইট জুড়ে ৪০,০০০টি ইমেজ স্লট পূরণ করছেন, এবং তাদের প্রতিটির লাইসেন্স, ক্রেডিট, সাইজ ঠিক থাকতে হবে এবং পাশেরটির থেকে ভিন্ন হতে হবে। এই ভলিউমে প্রশ্নটা আর “কোন ছবি?” থাকে না, প্রশ্নটা হয়ে যায় “স্থাপত্য (architecture) কী হবে?”
এই আর্টিকেলটি সেই API-ভিত্তিক উত্তর: এমন একটি রিকোয়েস্ট প্যাটার্ন যা কল সংখ্যা দুই অর্ডার অফ ম্যাগনিটিউড কমিয়ে দেয়, এমন একটি ওয়ার্কার যা রেট লিমিট সহ্য করে টিকে থাকে, এমন একটি ডিডুপ্লিকেশন যা ১০,০০০ পৃষ্ঠার সাইটকে একই স্টক ফটোর দেয়াল দেখতে হওয়া থেকে রক্ষা করে — এবং ছবি আপনার জন্য কী করতে পারে না তার একটি সৎ আলোচনা।
১০,০০০ আর্টিকেলে প্রকৃত বাধাটি কোথায়
ভলিউমে প্রকাশ করা টিমগুলো — প্রোগ্রাম্যাটিক SEO এস্টেট, মার্কেটপ্লেস ক্যাটাগরি পেজ, অ্যাগ্রিগেটর, অ্যাফিলিয়েট নেটওয়ার্ক, একাধিক ক্লায়েন্টের কনটেন্ট চালানো এজেন্সি, একটি আর্টিকেলকে ২০টি ভাষায় রূপান্তরিত করা লোকালাইজেশন পাইপলাইন — সবাই একই ক্রমে একই তিনটি দেয়ালে আটকায়:
- কল সংখ্যা। প্রতি ইমেজ স্লটের জন্য একটি সার্চ মানে ১০,০০০টি চার-ছবি-ওয়ালা আর্টিকেলের জন্য ৪০,০০০টি API কল। প্রতিটি প্রোভাইডার আপনাকে এই সংখ্যার ভিত্তিতেই দাম নির্ধারণ, থ্রটল এবং রিভিউ করে।
- পুনরাবৃত্তি। মোটামুটি ত্রিশতম পৃষ্ঠার কাছাকাছি একই ছবি আবার দেখা দিতে শুরু করে। তিন হাজারতম পৃষ্ঠায় গিয়ে আপনার সাইটের একটি ভিজ্যুয়াল স্বাক্ষর তৈরি হয়ে যায়: বাল্কে তৈরি।
- সমন্বয়। একটি ক্লাস্টারের একশোটি আর্টিকেলকে একটি সিরিজের মতো দেখাতে হবে, একশোটি অসংশ্লিষ্ট Pinterest বোর্ডের মতো নয় — এবং একই আর্টিকেলের পরবর্তী লোকেলে একই ছবি পুনরায় ব্যবহার করা উচিত, অন্য ভাষায় আবার সার্চ করা নয়।
খেয়াল করুন কোনটি এই তালিকায় নেই: ভালো ছবি খুঁজে পাওয়া। একটি সিমান্টিক ইঞ্জিন প্রায় ১৩০ মিলিসেকেন্ডে ব্যবহারযোগ্য প্রার্থীদের একটি পৃষ্ঠা ফেরত দেয়। রিট্রিভাল সমস্যাটি সমাধান হয়েই গেছে; ডিস্ট্রিবিউশন সমস্যা তো হয়নি। নিচের সবকিছুই ডিস্ট্রিবিউশন নিয়ে।
নির্দিষ্ট করে এই ভলিউমে কেন প্রকৃত ছবি
জেনারেশনের বদলে ফটোগ্রাফির পক্ষে যুক্তি ভলিউম বাড়ার সাথে সাথে আরও শক্তিশালী হয়, যার কারণগুলো মূলত নান্দনিক নয়, বরং অপারেশনাল।
| মাসে ৪০,০০০ ছবিতে | জেনারেট করা | সার্চ করা |
|---|---|---|
| প্রতি ছবির সময় | সেকেন্ড থেকে মিনিট, তার সাথে প্রত্যাখ্যাত প্রচেষ্টাগুলোও | একটি রিকোয়েস্ট (~১৩০ ms) পুরো একটি ক্লাস্টার কভার করে |
| খরচের নির্ধারক | প্রতি ছবিতে, চিরকাল | প্রতি রিকোয়েস্টে — এবং একটি রিকোয়েস্ট ~২৫টি আর্টিকেলকে সার্ভ করে |
| আপনি যে মেটাডেটা পান | কিছুই না। alt টেক্সট এবং ডাইমেনশন আপনাকে নিজেই লিখতে হয় | ডাইমেনশন, প্রধান রঙ, ব্লার হ্যাশ, ক্যাপশন খসড়া, ক্রেডিট লাইন |
| প্রোভেন্যান্স | ২ আগস্ট ২০২৬ থেকে EU AI Act অনুযায়ী মেশিন-চিহ্নিত সিনথেটিক1 | একজন নামসহ ফটোগ্রাফার, একটি তারিখ, একটি সোর্স URL যা পাঠক খুলে দেখতে পারেন |
| ব্যর্থতার ধরন | বিশ্বাসযোগ্য কিন্তু ভুল খুঁটিনাটি, অভিন্ন হাউস স্টাইল | ব্রিফের সাথে কিছুই মেলেনি — আপনি শূন্য ফলাফল পান, যা সামলানো সম্ভব |
মেটাডেটার সারিটিই পাইপলাইন নির্ধারণ করে। প্রতিটি সার্চ ফলাফল এমনিতেই width,
height, blur_hash, color_hex, alt_description
এবং একটি রেডি-মেড attribution.html স্ট্রিং বহন করে — যা একটি টেমপ্লেটিং ইঞ্জিনের
জন্য ঠিক সেই পেলোড যা প্লেসহোল্ডার এবং ক্রেডিটসহ লেআউট-শিফট-মুক্ত একটি <img>
তৈরি করতে দরকার। জেনারেটেড ইমেজ আপনাকে শুধু একটি ফাইল দেয় এবং বাকি ছয়টি ফিল্ড আবিষ্কার করার
দায়িত্ব আপনার উপর ছেড়ে দেয়।
যেখানে জেনারেশন এখনও স্কেলে জিতে যায়। বিমূর্ত ট্যাক্সোনমির (“ক্লাউড মাইগ্রেশন”, “ইনডেক্স ফান্ড”) জন্য ক্যাটাগরি-স্তরের ইলাস্ট্রেশন, ডায়াগ্রাম, এবং ব্র্যান্ডের কারণে পুরো এস্টেট জুড়ে পুনরাবৃত্ত রাখতে চাওয়া একটি হাউস স্টাইল। বড় পাবলিশাররা সাধারণত যে বিভাজনে এসে থামেন: বাস্তব জগতে যা কিছু আছে তার জন্য ফটোগ্রাফ, যা শুধু যুক্তিতে বিদ্যমান তার জন্য জেনারেটেড আর্ট। আমরা এই বিভাজনের পূর্ণ যুক্তি দিয়েছি প্রতিটি প্রকাশিত আর্টিকেলকে কীভাবে ইলাস্ট্রেট করবেন সেই আর্টিকেলে।
একটি রিকোয়েস্ট, একশো ছবি
এটিই একমাত্র পরিবর্তন যা গণনাটির পুরো চেহারা বদলে দেয়। সার্চ এন্ডপয়েন্ট per_page
১০০ পর্যন্ত নেয়, এবং কার্সর পেজিনেশন আপনাকে একই র্যাঙ্কড ফলাফল সেটের মধ্য দিয়ে
এগিয়ে যেতে দেয়। তাই কাজের একক একটি ছবি নয়, একটি পুল।
curl -sG "https://api.pexafy.com/api/v1/search/photos" \
-H "X-Api-Key: $PEXAFY_API_KEY" \
--data-urlencode "q=a woman signing paperwork with an insurance agent at a kitchen table in her home" \
--data-urlencode "per_page=100" \
--data-urlencode "orientation=landscape" \
--data-urlencode "score_threshold=0.5" \
--data-urlencode "fields=photo_id,urls,width,height,blur_hash,alt_description,attribution"
# → { "success": true, "data": [ …১০০টি পর্যন্ত ছবি… ],
# "pagination": { "per_page": 100, "has_more": true, "next_cursor": "eyJyc…" },
# "meta": { "took_ms": 122.33, "request_id": "7ff1fb9b-…" } }
এখানে তিনটি প্যারামিটার চুপচাপ বড় ভূমিকা পালন করছে স্কেলে:
fields— একটি স্পার্স ফিল্ডসেট। আপনার টেমপ্লেট যে সাতটি ফিল্ড রেন্ডার করে সেগুলো চান, তাহলে রেসপন্স আর প্রতিটি ছবির দীর্ঘ AI বিবরণ পাঠানো বন্ধ করে দেয়। ৪০,০০০ ছবি জুড়ে এটাই স্ট্রিম হওয়া বিল্ড আর সোয়াপ হওয়া বিল্ডের মধ্যে পার্থক্য।score_threshold— একটি ১০০-ফলাফলের পৃষ্ঠার লেজের অংশ, সংজ্ঞা অনুযায়ীই, মাথার অংশের চেয়ে কম প্রাসঙ্গিক। একটি সীমা নির্ধারণ মানে একটি পাতলা ক্লাস্টার ১০০টি মাঝারি মানের ছবির বদলে ৩৪টি ছবি ফেরত দেয়, এবং আপনার পাইপলাইন সেটি প্রকাশ করার বদলে সেই মতো প্রতিক্রিয়া জানাতে পারে।next_cursor— যখন একটি ক্লাস্টারের সত্যিই ৩০০টি ছবি দরকার হয়, তখন ওভারল্যাপ করা তিনটি আলাদা কোয়েরি চালানোর বদলে একই র্যাঙ্কড সেট পেজিনেট করুন।
আর এই হলো গণনাটি, মাসে চারটি করে ছবিসহ ১০,০০০ পৃষ্ঠা প্রকাশ করা একটি সাইটের জন্য:
| কৌশল | রিকোয়েস্ট / মাস | ফ্রি প্ল্যানের রেট লিমিটে একবারে সম্পন্ন করতে20 রিকোয়েস্ট/মিনিট | ফ্রি মাসিক কোটায় ফিট হয়? |
|---|---|---|---|
| প্রতি ইমেজ স্লটে একটি সার্চ | ৪০,০০০ | 33 hours | না — একটি পেইড প্ল্যান দরকার |
| প্রতি আর্টিকেলে একটি সার্চ | ১০,০০০ | 8 hours | না — একটি পেইড প্ল্যান দরকার |
| ~২০টি আর্টিকেলের একটি ক্লাস্টারে একটি সার্চএই আর্টিকেলের প্যাটার্ন | ৫০০ | 25 minutes | হ্যাঁ |
তিনটি সারিতেই একই ৪০,০০০টি প্রকাশিত ছবি। একমাত্র যা পরিবর্তিত হয়েছে তা হলো লুপটি কোথায় বসানো হয়েছে।
পুল আর অ্যাসাইন: যে প্যাটার্ন স্কেল করে
পুরো আর্কিটেকচারটি দুটি পর্যায়ে বিভক্ত যা ভিন্ন ভিন্ন ফ্রিকোয়েন্সিতে চলে, এবং তাদের মধ্যে একটি টেবিল থাকে:
┌─────────── সাপ্তাহিক চলে, ~৫০০ রিকোয়েস্ট ─────────┐
topic clusters ──▶ POOL প্রতি ক্লাস্টারে একবার সার্চ, per_page=100
│ └─▶ প্রতি ক্লাস্টারে ১০০টি ছবির সারি সংরক্ষণ
└──────────────────┬───────────────────────────────┘
▼
image_pool টেবিল
(cluster, photo_id, urls, blur_hash,
alt, credit, used_by_page, used_at)
│
┌──────────────────┴──── প্রতি প্রকাশনায় চলে, ০ রিকোয়েস্ট ──┐
article ─────────▶ ASSIGN এই ক্লাস্টারের জন্য সেরা অব্যবহৃত
│ সারি বেছে নিন, ব্যবহৃত চিহ্নিত করুন, রেন্ডার করুন
└────────────────────────────────────────────────────────┘
এতে যা পাওয়া যায়, ভলিউমে গুরুত্বের ক্রম অনুসারে:
- প্রকাশনা কখনো API-তে আটকে থাকে না। অ্যাসাইনমেন্ট একটি ডেটাবেস রিড। আপনার CMS সেভ হুক, আপনার স্ট্যাটিক বিল্ড এবং আপনার ভোর ৩টার বাল্ক ইম্পোর্ট — সবই স্থানীয় গতিতে, অফলাইনে, পথে কোনো রেট লিমিট ছাড়াই চলে।
- ডিডুপ্লিকেশন বিনামূল্যে এবং নিখুঁত।
used_by_page IS NULLএই পুরো ফিচার। দুটি পৃষ্ঠা একই ছবি নিতে পারে না, কারণ অ্যাসাইনমেন্ট একটি ট্রানজ্যাকশন, কোনো র্যাঙ্কিং হিউরিস্টিক নয়। - পুনরায় চালানো সস্তা এবং আইডেম্পোটেন্ট। একটি ক্লাস্টারের পুল কম পড়লে বা
আপনি নতুন ফটোগ্রাফি চাইলে (
after_date, বাsort_by=newest) সেটি রিফ্রেশ করুন — একটি রিকোয়েস্ট, এবং ইতিমধ্যে প্রকাশিত কিছুই নড়ে না। - লোকেল বিনামূল্যে আসে। একটি আর্টিকেলের ২০টি অনুবাদ আপনার মডেলে একটি পৃষ্ঠা,
২০টি রেন্ডারিং সহ; তারা একই বরাদ্দকৃত
photo_idশেয়ার করে এবং আপনাকে কখনো দুবার সার্চ করতে হয় না। আপনি যখন সত্যিই স্থানীয়ভাবে তোলা লুক চান, তখন সেই ক্লাস্টারের জন্য পুল কোয়েরিটি টার্গেট ভাষায় চালান — ইঞ্জিন ১০০টিরও বেশি ভাষায় একটি বাক্য নেয়।
কোডে ওয়ার্কার
মোটামুটি ষাট লাইন। পুল ফিলারই একমাত্র অংশ যা নেটওয়ার্কের সাথে কথা বলে, তাই এটিই একমাত্র অংশ
যেখানে সতর্ক থাকতে হয় — কনকারেন্সি সীমাবদ্ধ, 429 সম্মানিত, ফলাফল একটি ট্রানজ্যাকশনে
লেখা।
import asyncio, os, time, httpx
SEARCH = "https://api.pexafy.com/api/v1/search/photos"
KEY = os.environ["PEXAFY_API_KEY"]
FIELDS = "photo_id,urls,width,height,blur_hash,alt_description,attribution"
# ফ্রি = 20 রিকোয়েস্ট/মিনিট। এক নিচে থাকুন। পেসার GLOBAL: কনকারেন্সি
# গেটের ভেতরে পার-টাস্ক স্লিপ থাকলে N ওয়ার্কার N× হারে ফায়ার করবে।
PER_MIN = 19
gate = asyncio.Semaphore(4) # সচল কানেকশন
_lock = asyncio.Lock()
_slot = 0.0 # শেয়ার্ড টাইমলাইনে পরবর্তী ফাঁকা মুহূর্ত
async def pace():
"""পুরো ফ্লিট জুড়ে প্রতি 60/PER_MIN সেকেন্ডে একটি রিকোয়েস্ট স্লট দিন।"""
global _slot
async with _lock:
now = time.monotonic()
_slot = max(now, _slot) + 60 / PER_MIN
wait = _slot - now
if wait > 0:
await asyncio.sleep(wait)
class QuotaExhausted(Exception): ... # মাসিক: রিট্রাই করে লাভ নেই
async def fill_pool(client, cluster) -> list[dict]:
"""একটি রিকোয়েস্ট → একটি টপিক ক্লাস্টারের জন্য ১০০টি পর্যন্ত ক্রেডিটযুক্ত ছবি।"""
for attempt in range(4):
await pace()
async with gate:
r = await client.get(
SEARCH,
headers={"X-Api-Key": KEY},
params={
"q": cluster["camera_brief"], # একটি দৃশ্য, কিওয়ার্ড নয়
"per_page": 100,
"orientation": "landscape",
"score_threshold": 0.5,
"fields": FIELDS,
},
timeout=15,
)
if r.status_code == 429:
retry_after = r.headers.get("Retry-After")
if retry_after is None: # Retry-After নেই ⇒ মাসিক কোটা,
raise QuotaExhausted(cluster["id"]) # পুরো রান বন্ধ করুন
await asyncio.sleep(int(retry_after)) # প্রতি মিনিটে: এটা পরিষ্কার হয়ে যায়
continue
r.raise_for_status()
return r.json()["data"]
return [] # লগ করুন; পুল পুরনো সারিগুলো ধরে রাখে
async def refill(clusters):
async with httpx.AsyncClient(http2=True) as client:
pools = await asyncio.gather(*(fill_pool(client, c) for c in clusters))
for cluster, photos in zip(clusters, pools):
db.upsert_pool(cluster["id"], photos) # photo_id-তে ON CONFLICT DO NOTHING
এখানে দুটি খুঁটিনাটি বিষয় আছে যা একটি ওয়ার্কারকে অ্যাটেন্ডেড ছাড়া চলার আর আপনাকে মাঝরাতে
জাগিয়ে তোলার মধ্যকার পার্থক্য গড়ে দেয়। পেসারটি গ্লোবাল, প্রতি-টাস্ক নয়:
কনকারেন্সি গেটের ভেতরে স্লিপ বসানো একটি ক্লাসিক ভুল — চারটি ওয়ার্কার প্রত্যেকে তাদের নিজের
রিকোয়েস্টের পর ৩ সেকেন্ড থামলে প্রতি ৩ সেকেন্ডে চারটি রিকোয়েস্ট তৈরি হয়, প্রায় প্রতি
মিনিটে ৭৫টি, এবং ফ্রি প্ল্যান সাথে সাথে 429 ফেরত দেওয়া শুরু করে। আর দুই ধরনের
429 একই প্রাণী নয়: প্রতি-মিনিটেরটি Retry-After বহন করে এবং নিজে থেকে
পরিষ্কার হয়ে যায়, মাসিক-কোটারটি এটি বহন করে না এবং কখনো করবেও না — সেটি রিট্রাই করা একটি
দেয়ালের বিরুদ্ধে লুপ চালানোর মতো।
অ্যাসাইনমেন্ট, যা প্রতি প্রকাশনায় চলে, নেটওয়ার্ক কখনো ছোঁয় না:
# স্বতন্ত্রতা কোয়েরির সতর্কতা দিয়ে নয়, স্কিমা দিয়ে নিশ্চিত করা হয়।
# একটি ছবি একাধিক ক্লাস্টার পুলে থাকতে পারে; এটি শুধু একবার PUBLISHED হতে পারে।
# CREATE UNIQUE INDEX one_use_per_photo ON image_pool (photo_id)
# WHERE used_by_page IS NOT NULL;
def assign_hero(page_id: str, cluster_id: str) -> dict | None:
for _ in range(5): # রেস হারলেই পরবর্তী ছবি নেওয়া হয়
try:
return db.query_one("""
UPDATE image_pool p SET used_by_page = %s, used_at = NOW()
WHERE p.id = (
SELECT c.id FROM image_pool c
WHERE c.cluster_id = %s AND c.used_by_page IS NULL
AND NOT EXISTS ( -- অন্য কোনো ক্লাস্টার ব্যবহার করেছে?
SELECT 1 FROM image_pool u
WHERE u.photo_id = c.photo_id
AND u.used_by_page IS NOT NULL
)
ORDER BY c.rank ASC
LIMIT 1 FOR UPDATE SKIP LOCKED -- প্যারালাল ওয়ার্কারের সাথে নিরাপদ
)
RETURNING photo_id, urls, width, height, blur_hash, alt, credit
""", [page_id, cluster_id])
except UniqueViolation: # দুটি ক্লাস্টার একই মিলিসেকেন্ডে দাবি করেছে
continue
return None # পুল শুকনো → ব্রিফ প্রশস্ত করুন, রিফিল করুন
# রেন্ডারিং: প্রতিটি অ্যাট্রিবিউট সারি থেকে আসে — কোনো লেআউট শিফট নেই, অনুমান নেই।
# <img src="{urls[regular]}" width="{width}" height="{height}"
# alt="{alt}" loading="lazy" style="background:{blur_placeholder}">
# <figcaption>{credit}</figcaption>
দুটি ব্যর্থতার ধরন, দুটি প্রক্রিয়া, এবং দুটোই দরকার। FOR UPDATE SKIP LOCKED একটি
ক্লাস্টারের মধ্যে কনকারেন্সি সামলায়: প্যারালালভাবে পৃষ্ঠা তৈরি করুন — এবং এই ভলিউমে আপনি
করবেনই — এবং দুটি ওয়ার্কার একই মিলিসেকেন্ডে একই শীর্ষ-র্যাঙ্কড ছবির জন্য হাত বাড়ায়।
আংশিক ইউনিক ইনডেক্স এমন একটি জিনিস সামলায় যা সবাই ভুলে যায়: একই ছবি বৈধভাবেই একাধিক
ক্লাস্টারের পুলে দেখা দিতে পারে, কারণ পার্শ্ববর্তী ক্লাস্টারগুলো (“ভাড়াটেদের জন্য হোম ইনস্যুরেন্স”,
“...জমিদারদের জন্য”) ওভারল্যাপিং ফলাফল ফেরত দেয়। প্রতি-ক্লাস্টার
used_by_page IS NULL চেক এই বিষয়ে অন্ধ — প্রতিটি ক্লাস্টার মনে করে ছবিটি খালি আছে।
NOT EXISTS কোয়েরিটিকে সৎ রাখে, এবং ইনডেক্স কনকারেন্সিতে এটিকে ভুল হতে দেওয়া অসম্ভব
করে তোলে: রেসে হেরে যাওয়া পক্ষ একটি ভায়োলেশন পায়, পুনরায় চেষ্টা করে, এবং পরবর্তী ছবি নেয়। এটি
ছাড়া, “কোনো দুটি পৃষ্ঠা একই হিরো শেয়ার করে না” একটি দাবি মাত্র, নিশ্চয়তা নয়।
বিল্ড চলাকালীন একটি পুল শুকিয়ে গেলে, সবচেয়ে ভালো ফলব্যাক হলো পুনরাবৃত্তি না করে প্রশস্ত করা: ক্যামেরা ব্রিফের শেষ ধারাটি বাদ দিন, পুল কোয়েরিটি একবার আবার চালান, এবং তারপরই কেবল সবচেয়ে পুরনো-বরাদ্দকৃত ছবিটি পুনরায় ব্যবহার করুন — এই কঠোর নিয়মসহ যে এটি কখনোই একই ক্লাস্টারের কোনো পৃষ্ঠায় বসবে না।
একটি ক্লাস্টার, একটি পুল: একটি প্রকৃত কোয়েরি
একটি তুলনামূলক এস্টেট নিন: হোম ইনস্যুরেন্স, বিশটি পৃষ্ঠা — “ভাড়াটেদের জন্য হোম ইনস্যুরেন্স”, “...জমিদারদের জন্য”, “একটি পলিসি আসলে কী কভার করে”, বারোটি শহর পৃষ্ঠা, তিনটি ক্লেইম গাইড। ক্লাস্টারের জন্য একটি ক্যামেরা ব্রিফ, একটি রিকোয়েস্ট, এবং এই হলো পুলের মাথা, ১২২ ms-এ ফেরত এসেছে:
per_page=100 চাইছে এবং বাকিগুলো রেখে দিচ্ছে।
এই ঠিক সার্চটি চালান →
কোডের চেয়ে ব্রিফগুলোই বেশি গুরুত্বপূর্ণ। বাস্তব ক্লাস্টারের এস্টেটে দুটি নিয়ম টিকে যায়:
ব্রিফটি ক্লাস্টার-এর জন্য লিখুন, পৃষ্ঠার জন্য নয় (প্রোগ্রাম্যাটিক সেটে পৃষ্ঠার শিরোনাম
প্রায় অভিন্ন, তাই প্রতি-পৃষ্ঠা ব্রিফ প্রায় অভিন্ন ছবি ফেরত দেয়), এবং টপিক নয় বরং এমন একটি দৃশ্য
বর্ণনা করুন যা একটি ক্যামেরা তুলতে পারত — home insurance কেবল ডকুমেন্ট ক্লোজ-আপ
আর কিছুই ফেরত দেয় না। এই ব্রিফগুলো তৈরি করার কপি-পেস্ট করার মতো প্রম্পট আমরা প্রকাশ করেছি
প্রতিটি পোস্ট ইলাস্ট্রেট করার আর্টিকেলে।
২০০-পৃষ্ঠার সিরিজকে ভিজ্যুয়ালি সংগতিপূর্ণ রাখা
পুনরাবৃত্তির বিপরীত ব্যর্থতা হলো অসংগতি: একটি ক্লাস্টারের বিশটি পৃষ্ঠা, প্রতিটিতে প্রযুক্তিগতভাবে
সঠিক একটি ছবি, বিশটি ভিন্ন ভিজ্যুয়াল রেজিস্টারে। সমাধান একটি মাত্র এন্ডপয়েন্ট —
GET /photos/{id}/similar — পিলার পেজের জন্য আপনি যে ছবিটি অনুমোদন করেছেন সেটির
উপর একবার চালান:
ব্র্যান্ড সংগতি প্রয়োজন হলে পুল কোয়েরিতে যুক্ত করার মতো আরও দুটি লিভার আছে:
color_hex সহ একটি color_tolerance পুরো এস্টেটকে একটি প্যালেটের মধ্যে
রাখে, এবং photographer একটি ক্লাস্টারকে একজন নির্দিষ্ট ফটোগ্রাফারের কাজের সাথে
আটকে দেয়। দুটোই সাধারণ কোয়েরি প্যারামিটার — সার্চ ফিল্টারে কোনো প্ল্যান-ভিত্তিক বাধা নেই।
রেট লিমিট, কোটা, এবং প্রকৃত খরচ
প্ল্যানটি বার্স্ট-এর ভিত্তিতে বেছে নিন, ভলিউমের ভিত্তিতে নয়। একবার পুল করা শুরু করলে, প্রায় সবার জন্যই মাসিক কোটা আর সীমাবদ্ধতা হয়ে থাকে না; আপনার প্ল্যান নির্ধারণ করে একটি সম্পূর্ণ রিফ্রেশ কত দ্রুত শেষ হতে হবে তার উপর।
| প্ল্যান | রিকোয়েস্ট / মাস= রিফ্রেশযোগ্য ক্লাস্টার | রেট লিমিট | API কী | ১,০০০টি ক্লাস্টার রিফ্রেশওয়াল-ক্লক, একবার |
|---|---|---|---|---|
| Free — $0 | 5,000 | 20 / মিনিট | 1 | 50 min |
| Starter — $5 | 25,000 | 30 / মিনিট | 3 | 34 min |
| Pro — $19 | 100,000 | 60 / মিনিট | 5 | 17 min |
| Team — $99এজেন্সি: প্রতি ক্লায়েন্টে একটি কী | 500,000 | 200 / মিনিট | 25 | 5 min |
| Business — $249 | 1,000,000 | 300 / মিনিট | unlimited | 4 min |
| Enterprise — $599 | 2,000,000 | 500 / মিনিট | unlimited | 2 min |
প্রথম কলামটি ক্লাস্টার হিসেবে পড়ুন: একটি পুলিং রিকোয়েস্ট একটি ক্লাস্টার পূরণ করে, তাই মাসিক কোটা হলো আপনি কতগুলো ক্লাস্টার রিফ্রেশ করতে পারেন, এবং শেষ কলামটি হলো ১,০০০-ক্লাস্টারের একটি এস্টেট জুড়ে সেই প্ল্যানের রেট লিমিটে একটি সম্পূর্ণ পাস করতে কত সময় লাগে। দুটোই প্রাইসিং ক্যাটালগ থেকে লাইভ ভ্যালু — প্রাইসিং পৃষ্ঠায় একই রেট লিমিট প্রতি মিনিটের বদলে প্রতি ঘণ্টায় উল্লেখ করা আছে।
তাহলে মাসে ১০,০০০ পৃষ্ঠার একটি এস্টেট, পুলড প্যাটার্নে, ~৫০০টি রিকোয়েস্ট ব্যয় করে এবং কিছুই পরিশোধ করে না। যে জিনিসটি টিমগুলোকে টেবিলের উপরের দিকে ঠেলে দেয় তা খুব কমই ভলিউম হয় — বরং তিনটি জিনিসের একটি: একটি বিদ্যমান এস্টেটের সম্পূর্ণ পুনঃ-ইলাস্ট্রেশন একরাতে, প্রতি-ক্লায়েন্ট কী আইসোলেশন (একটি এজেন্সি চায় প্রতিটি ক্লায়েন্টের জন্য একটি কী যাতে শেয়ার্ড-সিক্রেট জিমন্যাস্টিক্স ছাড়াই ব্যবহার শনাক্তযোগ্য হয়), অথবা ঘণ্টার বদলে মিনিটে পরিমাপ করা একটি বিল্ড উইন্ডো।
দুটি অপারেশনাল খুঁটিনাটি যা এক রাতের ডিবাগিং বাঁচায়। প্রতিটি রেসপন্সে
X-RateLimit-Limit এবং X-RateLimit-Remaining থাকে, তাই একটি ওয়ার্কার
অনুমান না করে নিজেকে গতিতে রাখতে পারে। আর একটি প্রকৃত প্রতি-মিনিট রেট লিমিট এমন
429 Retry-After বহন করে, অথচ একটি মাসিক-কোটা ব্লক ইচ্ছাকৃতভাবে এটি
বহন করে না — সেটি রিট্রাই করে পরিষ্কার হবে না, এবং যে ওয়ার্কার এই দুটির পার্থক্য বোঝে
সে এমন একটি এন্ডপয়েন্টে আঘাত করা বন্ধ করে দেয় যার আর কিছু দেওয়ার নেই।
যখন এজেন্ট প্রকাশনার কাজ করে
আপনার কনটেন্ট যদি একটি এজেন্ট দিয়ে তৈরি হয় — এবং এই ভলিউমে ক্রমবর্ধমানভাবে তা হয়েই থাকে —
পুলিং লেয়ারটি হারিয়ে যায় না, এটি স্থানান্তরিত হয়। এজেন্টকে সার্চ টুল দিন এবং এটি ক্লাস্টার
ব্রিফটি কনটেক্সটে রাখা অবস্থায়ই পুল পূরণ করে ফেলবে,
Model Context Protocol সার্ভার mcp.pexafy.com/mcp ব্যবহার করে:
তিনটি টুল, search_photos (একটি বাক্য), search_photos_by_image
(একটি রেফারেন্স ইমেজ, ঐচ্ছিকভাবে একটি বাক্যসহ) এবং get_similar_photos (উপরে উল্লেখিত
সিরিজ-সংগতির টুল)।
# Claude Code / CI রানার
claude mcp add --transport http pexafy https://mcp.pexafy.com/mcp \
--header "Authorization: Bearer $PEXAFY_API_KEY"
# অথবা .mcp.json কমিট করুন যাতে ফ্লিটের প্রতিটি ওয়ার্কার এটি উত্তরাধিকারসূত্রে পায়
{
"mcpServers": {
"pexafy": {
"type": "http",
"url": "https://mcp.pexafy.com/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
}
স্কেলে যে নির্দেশনা কাজ করে তা প্রতি-আর্টিকেল নয়, একটি ব্যাচ নির্দেশনা — এবং এটি উপরের দুটি পর্যায়ের সাথে হুবহু মিলে যায়:
You এখানে কনটেন্ট ক্যালেন্ডার থেকে ১২টি টপিক ক্লাস্টার আছে। প্রতিটির জন্য,
একটি ক্যামেরা ব্রিফ লিখুন, ১০০টি ল্যান্ডস্কেপ ছবি টানুন, এবং
পুলটি Postgres-এ লিখুন। 0.5-এর উপরে ৪০টির কম ফলাফল ফেরত দেয়
এমন যেকোনো ক্লাস্টার ফ্ল্যাগ করুন — সেই ব্রিফগুলো নতুন করে লিখতে হবে।
Agent → search_photos(q="a woman signing paperwork with an insurance
agent at a kitchen table in her home", orientation="landscape")
← ১০০টি ছবি · ১২২ ms · থ্রেশহোল্ডের উপরে ৮৭টি
… আরও ১১টি ক্লাস্টার …
✓ ১১টি পুল লেখা হয়েছে (১,০৪৩টি ছবি, সবগুলো ক্রেডিটসহ)
⚠ "index fund rebalancing" → ১২টি ফলাফল। ব্রিফটি বিমূর্ত;
প্রস্তাবনা: "a person at a kitchen table checking figures on a
laptop with a notebook and coffee beside them"
এই শেষ লাইনটিই এজেন্টকে এই লুপে রাখার কারণ, একটি স্ক্রিপ্ট নয়: প্রোগ্রাম্যাটিক ইলাস্ট্রেশনের ব্যর্থতার ধরন হলো একটি খারাপ ব্রিফ, আর একটি খারাপ ব্রিফ ঠিক এমন একটি জিনিস যা একটি ভাষা মডেল লক্ষ্য করে নতুন করে লিখতে পারে। স্ক্রিপ্টগুলো অ্যাসাইনমেন্টের দায়িত্বে থাকে, যেখানে নির্ধারণযোগ্যতা গুরুত্বপূর্ণ এবং সৃজনশীলতা নয়।
ছবি যা ঠিক করতে পারে না
একটি সৎ আলোচনা, কারণ এটি যে ব্যর্থতার বর্ণনা দেয় তা ব্যয়বহুল। প্রকৃত, ক্রেডিটসহ ফটোগ্রাফি একটি পৃষ্ঠাকে উন্নত করে। এটি পাতলা কনটেন্টকে ভালো কনটেন্টে রূপান্তরিত করে না, এবং কোনো ইমেজ পাইপলাইন সার্চ ইঞ্জিন কীভাবে সেই পৃষ্ঠাগুলোকে বিচার করে তা পরিবর্তন করে না যেগুলো সাহায্য করার বদলে র্যাঙ্ক করার জন্যই বাল্কে তৈরি হয়। Google-এর স্প্যাম নীতি এটি সরাসরি উল্লেখ করে: স্কেলড কনটেন্ট অ্যাবিউজ কম মূল্যের অনেক পৃষ্ঠা তৈরি করাকে কভার করে অটোমেশন জড়িত থাকুক বা না থাকুক, এবং সেই পৃষ্ঠাগুলোর ইলাস্ট্রেশন এই বিচারের ক্ষেত্রে অপ্রাসঙ্গিক।2
তাই কার্যকর ফ্রেমিং সংকীর্ণ এবং সত্য: ফটোগ্রাফি এমন একটি পৃষ্ঠায় আপনি নিয়ন্ত্রণ করেন এমন একটি মান সংকেত যা ইতিমধ্যে বিদ্যমান থাকার যোগ্যতা রাখে। যেখানে এটি প্রমাণযোগ্যভাবে কাজে আসে:
- একজন পাঠক যাচাই করতে পারেন এমন প্রোভেন্যান্স। একজন ফটোগ্রাফারের নাম এবং একটি সোর্স URL সহ একটি ক্রেডিট লাইন এমন একটি দাবি যা যাচাই করা যায় — একজন অজানা লেখকের একটি পৃষ্ঠায় একটি অ্যাট্রিবিউশন-ছাড়া ছবির ঠিক বিপরীত।
- অ্যাক্সেসিবিলিটি ও Core Web Vitals। রেসপন্স থেকে পাওয়া
width/heightলেআউট শিফট দূর করে,blur_hashএকটি প্রকৃত প্লেসহোল্ডার দেয়, এবংalt_descriptionalt টেক্সটের একটি খসড়া যা আপনার টেমপ্লেট উদ্ভাবন করার বদলে উন্নত করতে পারে। ৪০,০০০টি স্লট দিয়ে গুণ করলে এটাই সাইটের সম্পূর্ণ ইমেজ-কোয়ালিটি গল্প। - বাল্কে তৈরি বলে না দেখানো। ডিডুপ্লিকেশন এবং সিরিজ-সংগতিই একটি এস্টেটকে ভিজ্যুয়াল স্বাক্ষর তৈরি হওয়া থেকে থামায়। এটি একটি বাস্তব উপলব্ধিগত খরচ যার বাস্তব পরিণতি আছে, এবং এটি সম্পূর্ণভাবে আপনার নিয়ন্ত্রণে।
আর লাইসেন্স এখনও ছবিটির উপর প্রযোজ্য। Pexafy API শর্তাবলীতে অ্যাট্রিবিউশন বাধ্যতামূলক নয় —
প্রতিটি ফলাফল রেন্ডার করার জন্য প্রস্তুত attribution.html এবং
attribution.plain সহ আসে — কিন্তু মূল লাইব্রেরি দ্বারা সংযুক্ত লাইসেন্স আপনার সেই
ছবির ব্যবহারের উপর প্রযোজ্য, এবং মাসে ৪০,০০০ ছবিতে, স্বয়ংক্রিয়ভাবে ক্রেডিট রেন্ডার করা পরে
অডিট করার চেয়ে সস্তা।
সোমবার থেকে কোথায় শুরু করবেন
- আপনার ব্যাকলগকে ক্লাস্টারে ভাগ করুন ১৫–৩০টি পৃষ্ঠার, যারা যুক্তিসঙ্গতভাবে একটি ফটো শুট শেয়ার করতে পারে। এটিই একমাত্র সত্যিকারের ম্যানুয়াল ধাপ, এবং এটি একটি প্রজেক্ট নয়, একটি স্প্রেডশিট।
- প্রতি ক্লাস্টারে একটি ক্যামেরা ব্রিফ লিখুন — একটি দৃশ্য, ১২ থেকে ২৫ শব্দ। একটি মডেল দিয়ে সেগুলো তৈরি করুন, তারপর পড়ুন; যে ব্রিফগুলো দৃশ্যের বদলে একটি টপিকের নাম দেয় সেগুলো এক নজরেই দেখা যায়।
- একটি পুল পূরণ করুন একটি একক
per_page=100রিকোয়েস্ট দিয়ে এবং এর মাথাটা চোখ দিয়ে দেখুন। শীর্ষ দশটি ব্যবহারযোগ্য না হলে, ব্রিফটি ভুল — ইঞ্জিন নয়। used_by_pageকলামটি যোগ করুন কিছু প্রকাশ করার আগেই। এখন এটি পাঁচ মিনিটের কাজ, পরে ৩,০০০টি লাইভ পৃষ্ঠা জুড়ে একটি মাইগ্রেশন।- পুরো জিনিসটি ফ্রি প্ল্যানে চালান যতক্ষণ না একটি বিল্ড উইন্ডো আপনাকে উপরে ঠেলে দেয়। মাসে ৫০০টি রিকোয়েস্টে, তাতে বেশ কিছুটা সময় লাগবে।
তথ্যসূত্র ও পাদটীকা
1 EU AI Act, অনুচ্ছেদ ৫০ — স্বচ্ছতার বাধ্যবাধকতা কার্যকর ২ আগস্ট ২০২৬ থেকে: সিনথেটিক ইমেজ, অডিও, ভিডিও বা টেক্সট তৈরি করা সিস্টেমের প্রোভাইডারদের অবশ্যই মেশিন-পাঠযোগ্য ফরম্যাটে আউটপুট চিহ্নিত করতে হবে এবং সেগুলোকে কৃত্রিমভাবে তৈরি হিসেবে শনাক্তযোগ্য করতে হবে। এটি AI প্রোভাইডার ও ডিপ্লয়ারদের বাধ্য করে; এটি কোনো ওয়েবসাইট কোন ছবি প্রকাশ করতে পারবে তার নিয়ম নয়।
2 Google Search স্প্যাম নীতি — স্কেলড কনটেন্ট অ্যাবিউজ: মূলত র্যাঙ্কিং ম্যানিপুলেট করার জন্য অনেক পৃষ্ঠা তৈরি করা এবং ব্যবহারকারীদের সামান্য মূল্য প্রদান করা, তা অটোমেশন, মানুষের প্রচেষ্টা বা উভয়ের সমন্বয়ে তৈরি হোক না কেন। ইলাস্ট্রেশনের মান সেই মূল্যায়নে কোনো নিয়ামক নয়, এবং ঠিক এই কারণেই এই আর্টিকেলটি দুটোকে আলাদা রাখে।
১৭ আগস্ট ২০২৬-এ যাচাইকৃত সোর্স: Google Search স্প্যাম নীতি · AI Act অনুচ্ছেদ ৫০ · Pexafy API ও MCP ডকুমেন্টেশন। প্ল্যানের সীমাগুলো Pexafy প্রাইসিং টেবিলের লাইভ মান; সার্চের সময়কাল (১২২ ms, ৪৮ ms) এবং দেখানো প্রতিটি ছবি একই দিনে ধারণ করা প্রকৃত API রেসপন্স।
প্রায়শই জিজ্ঞাসিত প্রশ্ন
হাজার হাজার প্রোগ্রাম্যাটিক SEO পেজের জন্য আমি কীভাবে ছবি পাব?
per_page=100 সহ একটি একক GET /api/v1/search/photos রিকোয়েস্ট একটি ক্লাস্টারের জন্য ১০০টি পর্যন্ত ক্রেডিটেড ছবি ফিরিয়ে দেয়; আপনি সেগুলো একটি পুল টেবিলে সংরক্ষণ করেন এবং প্রকাশের সময় প্রতি পেজে একটি করে অ্যাসাইন করেন। প্রতি পেজে চারটি ছবিসহ মাসে ১০,০০০ পেজের একটি এস্টেটের জন্য ৪০,০০০ এর বদলে প্রায় ৫০০টি রিকোয়েস্ট লাগে, যা ফ্রি প্ল্যানের (মাসে ৫,০০০ রিকোয়েস্ট) মধ্যেই ধরে যায়।বড় আকারের কনটেন্ট প্রোডাকশনের জন্য সেরা স্টক ফটো API কোনটি?
একই স্টক ফটো একাধিক পেজে দেখা যাওয়া কীভাবে বন্ধ করব?
photo_id সংরক্ষণ করুন এবং পুল থেকে ছবি ট্রানজ্যাকশনালভাবে দাবি করুন — SQL-এ, একটি UPDATE … WHERE used_by_page IS NULL … FOR UPDATE SKIP LOCKED। এভাবে ডিডুপ্লিকেশন সম্ভাব্যতার পরিবর্তে সঠিক হয়ে যায়, এবং সমান্তরাল বিল্ড ওয়ার্কারগুলো একই সেরা-র্যাঙ্কড ছবির জন্য প্রতিযোগিতা করতে পারে না। এটি মাত্র একটি কলাম, এবং হাজার হাজার লাইভ পেজ জুড়ে পরে এটি যোগ করার চেয়ে প্রথম দিনেই যোগ করা অনেক সস্তা।একটি API রিকোয়েস্ট সর্বোচ্চ কতগুলো ছবি ফেরত দিতে পারে?
per_page=100 ব্যবহার করে, এবং কার্সার পেজিনেশন আপনাকে আরও গভীর পুলের জন্য একই র্যাঙ্কড রেজাল্ট সেট ধরে হাঁটতে দেয়। এটিকে score_threshold এর সাথে মেলান যাতে একটি পাতলা ক্লাস্টার ১০০টি আলগা ম্যাচের বদলে ৪০টি শক্তিশালী ম্যাচ ফেরত দেয়, এবং fields এর সাথে যাতে শুধু সেই অ্যাট্রিবিউটগুলো ফেরত আসে যা আপনার টেমপ্লেট রেন্ডার করে, যা বড় আকারে রেসপন্স ছোট রাখে।বড় আকারে তৈরি পেজে প্রকৃত ছবি কি র্যাঙ্কিং ভালো করতে সাহায্য করে?
একটি AI এজেন্ট কি স্বয়ংক্রিয়ভাবে একটি ইমেজ পুল পূরণ করতে পারে?
mcp.pexafy.com/mcp-এর মাধ্যমে, যা search_photos, search_photos_by_image এবং photo_similar প্রকাশ করে। এজেন্টকে ক্লাস্টারের একটি তালিকা দিন এবং এটি প্রতিটির জন্য একটি করে ক্যামেরা ব্রিফ লেখে, পুলগুলো টেনে আনে এবং যেসব ব্রিফ খুব কম শক্তিশালী ম্যাচ ফেরত দিয়েছে সেগুলো চিহ্নিত করে — যা আসলে প্রোগ্রাম্যাটিক ইলাস্ট্রেশনের ব্যর্থতার প্রকৃত ধরন। অ্যাসাইনমেন্ট আপনার স্ক্রিপ্টেই থাকে, যেখানে সৃজনশীলতার চেয়ে নির্ধারণযোগ্যতা বেশি গুরুত্বপূর্ণ।