#PigData
How We Built Scraping AI: Turning 500+ Enterprise Projects Into a Self-Serve API
**From managed enterprise scraping at PigData to a high-scale developer API in six months.** > [!NOTE] > **TL;DR / Engineering Retrospective:** > > * **Origin:** PigData delivered 500+ custom enterprise scraping projects (Amazon, Honda, MUFG) via managed services before codifying core scraping patterns into a self-serve developer API. > * **Tech Stack:** Django REST Framework + Celery + RabbitMQ + PostgreSQL (`VersionedModel` optimistic locking) + S3 / MinIO storage. > * **Key Innovation:** A versioned state-machine pipeline (`InputState`) powering modular Crawlers, LLM Extractors (OpenAI / Gemini), and BM25 + Vector Rankers. > * **Zero-Risk Trial:** Get **200 free tokens** (no credit card required) at https://pig-data.jp/service/scraping-ai/. > ## The Origin Problem For years, PigData operated as a managed data extraction service in Japan, building bespoke scrapers for enterprise data pipelines. Whether extracting product catalogs or market intelligence, our engineers handled the end-to-end process. The problem? **Every project started from scratch.** Even when two clients needed similar data (e.g., e-commerce product listings), we were rebuilding identical parsing logic, browser automation routines, and anti-bot retry loops. We faced four core engineering bottlenecks: 1. **No economies of scale:** Every client required dedicated maintenance hours when target sites updated CSS selectors. 2. **Small developer teams were priced out:** Managed agency contracts have high monetary floors. 3. **Developers wanted self-serve APIs:** Modern engineering teams don't want a 3-week sales cycle—they want an API key and a 3-line SDK call. 4. **Siloed expertise:** Anti-bot bypasses and rate-limiting tricks learned in one project weren't automatically shared across systems. We needed an architecture capable of running 10 jobs or 10,000 concurrent crawling jobs on the exact same infrastructure. ## Architecture Overview We chose a Python stack centered around **Django REST Framework (DRF)** , **Celery** , **RabbitMQ** , and **PostgreSQL** : ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Django API │ ─────▶│ RabbitMQ │ ─────▶│ Celery Workers │ │ (DRF Layer) │ │ (Message Queue) │ │ (Distributed) │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ PostgreSQL State│ ◀───────────────────────────────│ S3 / MinIO │ │ (Optimistic Lock│ │ Data Exports │ └─────────────────┘ └─────────────────┘ ### Why Django & Celery in 2026? * **Battle-Tested Reliability:** Web scraping is heavily asynchronous. Django ORM paired with Celery + RabbitMQ provided proven distributed task queuing out of the box. * **Admin & Support Tooling:** Django's built-in Admin gave our team immediate visual access to monitor stuck tasks, retry failed extraction pipelines, and debug user jobs without building custom internal tooling. * **Productivity over Hype:** Staying in Python allowed us to leverage `httpx`, `BeautifulSoup`, `pydantic`, `openai`, and `google-genai` directly without cross-language serialization overhead. ## The State Machine Pipeline Design Every data extraction job follows a predictable lifecycle: [Keywords / Search Query] │ ▼ ┌─────────────┐ │ URL Finder │ (Discovers link graph up to max_depth) └─────────────┘ │ ▼ ┌─────────────┐ │ Crawler │ (Fetches HTML via httpx or headless browser) └─────────────┘ │ ▼ ┌─────────────┐ │ AI Ranker │ (Ranks pages via BM25 + Vector embeddings) └─────────────┘ │ ▼ ┌─────────────┐ │ LLM Extractor│ (Applies JSON Schema via GPT-4o / Gemini) └─────────────┘ │ ▼ [Structured JSON / CSV Export] We codified this workflow into a single state machine backed by our central `InputState` model: class InputState(VersionedModel): """Central state machine model for an extraction task.""" # Configuration & Instructions base_url = models.URLField(max_length=2048) user_instruction = models.TextField() schema_instruction = models.TextField() # Pipeline Execution State site_type = models.CharField(max_length=20, choices=[('general', 'General'), ('ec', 'E-Commerce')]) auto_flow = models.BooleanField(default=True) current_step = models.CharField(max_length=50, choices=PIPELINE_STEPS) # Step Status Trackers keyword_generator_status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING') url_finder_status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING') url_crawler_status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING') url_ranker_status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING') schema_generator_status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING') extraction_status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING') ## Hard Technical Challenges & Solutions ### 1. Solving Worker Race Conditions (Optimistic Locking) With dozens of Celery workers processing URLs concurrently, multiple workers attempted to update `InputState` status simultaneously, causing lost updates. **Solution:** We built optimistic locking into `VersionedModel`: class VersionedModel(models.Model): version = models.IntegerField(default=0) class Meta: abstract = True def save(self, *args, **kwargs): if self.pk: affected = self.__class__.objects.filter( pk=self.pk, version=self.version ).update(version=models.F('version') + 1, **kwargs.get('update_fields_dict', {})) if not affected: raise ConcurrencyError(f"Version conflict on {self.__class__.__name__} ID {self.pk}") self.version += 1 return super().save(*args, **kwargs) ### 2. High-Throughput Batch Upserts (`ConcurrencyManager`) Calling ORM `.save()` inside loops on 10,000 discovered URLs overwhelmed PostgreSQL. We implemented a custom `ConcurrencyManager`: class ConcurrencyManager(models.Manager): def bulk_claim_and_create(self, urls_data: list, state_id: int): """Batch upserts URLs using PostgreSQL bulk ON CONFLICT handling.""" existing_urls = set( self.filter(input_state_id=state_id, url__in=[u['url'] for u in urls_data]) .values_list('url', flat=True) ) new_objects = [ self.model(input_state_id=state_id, url=u['url'], status='PENDING') for u in urls_data if u['url'] not in existing_urls ] self.bulk_create(new_objects, batch_size=1000, ignore_conflicts=True) ### 3. Fair Ledger-Based Token Accounting Instead of complex billing per CPU second, we implemented a real-time transactional token ledger: class TokenLedger(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) amount = models.IntegerField() # Negative for debits, positive for credits action = models.CharField(max_length=50) # e.g., 'task.start', 'url.extractor' balance_after = models.IntegerField() timestamp = models.DateTimeField(auto_now_add=True) ## What the End-User Developer Experience Looks Like While our backend handles complex async state machines, celery queues, and token ledgers, developers interact with our official published PyPI package (scraping-ai): pip install scraping-ai from scraping_ai import ScrapingAIClient client = ScrapingAIClient(api_key="YOUR_API_KEY") # Extract web data directly in one step (polls until finished) data = client.extract( url="https://example.com/products", schema={"title": "string", "price": "number", "in_stock": "boolean"} ) print(data.results) ## Honest Retrospective: What We'd Do Differently Looking back at our 6-month journey: 1. **Start with v2 API Specs First:** Our v1 API bundled too many steps synchronously. Splitting v2 into clean async endpoints made client integration significantly smoother. 2. **Rate-Limiting on Day 1:** We initially relied on Celery concurrency limits, but dedicated per-domain rate limiters were necessary to prevent hitting target site blocks. 3. **Transparent Anti-Bot Expectations:** Smart Stealth works ~85% of the time, but extreme bot defenses require specialized headless setups. Volunteering these boundaries built stronger trust with developer teams. ## Try Scraping AI Free Stop writing fragile scrapers and fixing broken CSS selectors. * **Sign up & get 200 free tokens:** https://pig-data.jp/service/scraping-ai/ * **Explore API Documentation:** https://pig-data.jp/service/scraping-ai/docs/ * **PyPI Package:** https://pypi.org/project/scraping-ai/ * **Pricing Tiers:** Free (200 tokens) → Starter ($10 / 1,600 tokens) → Growth ($30 / 5,000 tokens) → Pro ($100 / 20,000 tokens) ### About the Team & Company **Scraping AI** (https://pig-data.jp/service/scraping-ai/) is developed and operated by **indigodata Inc.** , an AI venture subsidiary of **SMS DataTech Co., Ltd.** (Tokyo, Japan). Built upon PigData's track record of 500+ enterprise data extraction projects, Scraping AI provides a self-serve LLM extraction API for developers worldwide.
dev.to
September 15, 2026 at 1:38 AM
On the rise of #pigdata...
December 2, 2024 at 6:44 AM
AIと結びついた未来の仕事環境を形作るインディゴデータの挑戦#インディゴデータ#PigData#ヒトタスAI

インディゴデータが提案する新たなビジョン、ヒトとAIの共存を通じて、企業のデータ活用の未来がどのように変わるのかを探ります。
AIと結びついた未来の仕事環境を形作るインディゴデータの挑戦
インディゴデータが提案する新たなビジョン、ヒトとAIの共存を通じて、企業のデータ活用の未来がどのように変わるのかを探ります。
tokyo.publishing.3rd-in.co.jp
April 1, 2026 at 1:08 AM
Si toi aussi tu aimes rigoler en lisant du #bullshit de marketeux : https://t.co/d38wThN3dZ #PigData #BigTada
January 17, 2025 at 6:42 PM
Transforming Business with AI: Indigo Data's Vision for a Human-AI Partnership#Japan#Tokyo#AI_Partnership#Indigo_Data#PigData
Transforming Business with AI: Indigo Data's Vision for a Human-AI Partnership
Indigo Data is reshaping the way businesses interact with data through AI, aiming for a future where humans and AI collaborate seamlessly.
third-news.com
April 1, 2026 at 1:11 AM
株式会社インディゴデータが新法人向けAI『PigData スクレイピングAI』を発表#東京都#中央区#インディゴデータ#PigData#スクレイピングAI

株式会社インディゴデータが新たに発表した法人向けAI『PigData スクレイピングAI』は、効率的にデータ収集を実現する革新的なサービスです。
株式会社インディゴデータが新法人向けAI『PigData スクレイピングAI』を発表
株式会社インディゴデータが新たに発表した法人向けAI『PigData スクレイピングAI』は、効率的にデータ収集を実現する革新的なサービスです。
news.3rd-in.co.jp
June 30, 2026 at 1:23 AM
株式会社インディゴデータが目指すヒト+AIの新時代、夢を発信する取り組みとは#東京都#中央区#株式会社インディゴデータ#ヒトタスAI#PigData

株式会社インディゴデータは、AIとデータ活用を通じて企業の未来を形作る取り組みを行っています。ヒトとAIの新たな関係を探求するその姿勢に迫ります。
株式会社インディゴデータが目指すヒト+AIの新時代、夢を発信する取り組みとは
株式会社インディゴデータは、AIとデータ活用を通じて企業の未来を形作る取り組みを行っています。ヒトとAIの新たな関係を探求するその姿勢に迫ります。
news.3rd-in.co.jp
April 1, 2026 at 12:50 AM
#PigData Master duties for our Italo-Canadian dinner tonight.
November 10, 2024 at 9:48 AM
Goat cheese stuffed Lamb burgers, round two. #pigdata #geeksthatgrill
November 10, 2024 at 9:25 AM
My next weekend project: merge #BigData with #PigData. Capture meat temp and atmospheric data while smoking a good rack of ribs.
November 9, 2024 at 5:58 PM
Hello dinner. #pigdata #geeksthatgrill
November 9, 2024 at 5:55 PM
We’re almost there, these ribs look and smell delicious! #pigdata #geeksthatgrill
November 9, 2024 at 5:55 PM
Ribs on the grill and they look *fantastic*! pictures coming, thanks @ulfrasmussen for the mop sauce recipe. #pigdata #geeksthatgrill
November 9, 2024 at 5:55 PM
I desperately need to smoke some ribs this weekend, looking for a good sweet mopping sauce, any advice? #geeksthatgrill #pigdata
November 9, 2024 at 5:53 PM
Cherry smoked Salmon fillets on the BBQ tonight, look delicious! #geeksthatgrill #pigdata
November 9, 2024 at 5:30 PM
Seafood skewers on the grill #pigdata #geeksthatgrill
November 9, 2024 at 5:22 PM
On the BBQ tonight: greek style lamb burgers with homemade pita bread and tzatziki sauce. #PIGDATA #geeksthatgrill
November 9, 2024 at 5:21 PM
Lightning up the coals again! Bacon-wrapped chicken skewers on the BBQ #PIGDATA #geeksthatgrill
November 9, 2024 at 5:21 PM
#PIGDATA on tour!
November 9, 2024 at 5:20 PM
Low&Slow Roast beef sandwiches on the BBQ, first try, smells GUD! #pigdata #geeksthatgrill
November 9, 2024 at 5:19 PM
Bacon chicken sticks on the BBQ, under a stormy weather. Happy Easter! #PIGDATA
November 9, 2024 at 5:12 PM
My portable #PIGDATA rig :-)
November 9, 2024 at 5:12 PM
My #PIGDATA implementation is shaping right :-)
November 9, 2024 at 5:10 PM
Sensual massage :-) #PIGDATA
November 9, 2024 at 5:10 PM
Trimming ribs St. Louis style. #PIGDATA
November 9, 2024 at 5:10 PM