Skip to content

Repository files navigation

ShopSavvy Data API - Ruby SDK

Gem VersionRubyLicense: MITDocumentation

Official Ruby SDK for the ShopSavvy Data API. Access comprehensive product data, real-time pricing, and historical price trends across thousands of retailers and millions of products.

⚡ 30-Second Quick Start

# Installgeminstallshopsavvy-sdk# Userequire'shopsavvy_data_api'client=ShopsavvyDataApi.new(api_key: 'ss_live_your_api_key_here')product=client.get_product_details('012345678901')puts"#{product.data.name} - Best price: $#{client.get_current_offers('012345678901').data.min_by(&:price).price}"

🚀 Installation & Setup

Installation

Add to your Gemfile:

gem'shopsavvy-sdk'

Or install directly:

gem install shopsavvy-sdk

Get Your API Key

  1. Sign up: Visit shopsavvy.com/data
  2. Choose plan: Select based on your usage needs
  3. Get API key: Copy from your dashboard
  4. Test: Run the 30-second example above

Environment Setup

# For production, use environment variablesENV['SHOPSAVVY_API_KEY']='ss_live_your_api_key_here'# Initialize clientclient=ShopsavvyDataApi.new(api_key: ENV['SHOPSAVVY_API_KEY'])

📖 Complete API Reference

Client Configuration

# Basic configurationclient=ShopsavvyDataApi.new(api_key: 'ss_live_your_api_key_here',timeout: 30,# Request timeout in secondsbase_url: 'https://api.shopsavvy.com/v1'# Custom base URL)# Advanced configuration with retry logicclient=ShopsavvyDataApi.new(api_key: 'ss_live_your_api_key_here',timeout: 60,retry_attempts: 3,retry_delay: 1.0,user_agent: 'MyApp/1.0.0')# Configuration object approachconfig=ShopsavvyDataApi::Configuration.new(api_key: 'ss_live_your_api_key_here',timeout: 120,debug: true# Enable debug logging)client=ShopsavvyDataApi.with_config(config)

Product Lookup

Single Product

# Look up by barcode, ASIN, URL, model number, or ShopSavvy IDproduct=client.get_product_details("012345678901")amazon_product=client.get_product_details("B08N5WRWNW")url_product=client.get_product_details("https://www.amazon.com/dp/B08N5WRWNW")model_product=client.get_product_details("MQ023LL/A")# iPhone model numberputs"Product: #{product.data.name}"puts"Brand: #{product.data.brand}"puts"Category: #{product.data.category}"puts"ASIN: #{product.data.asin}"ifproduct.data.asinputs"Model: #{product.data.model}"ifproduct.data.modelputs"Description: #{product.data.description}"ifproduct.data.description

Bulk Product Lookup

# Process up to 100 products at once (Pro plan)identifiers=["012345678901","B08N5WRWNW","045496590048","https://www.bestbuy.com/site/product/123456","MQ023LL/A","SM-S911U"# iPhone and Samsung model numbers]products=client.get_product_details_batch(identifiers)products.data.eachdo |product|
puts"#{product.name} by #{product.brand} - #{product.category}"puts" Identifiers: #{product.identifiers}"ifproduct.identifiersend# Handle potential errors in batch processingproducts.data.each_with_indexdo |product,index|
ifproduct.nil?puts"Failed to find product: #{identifiers[index]}"elseputs"✓ Found: #{product.name}"endend

Real-Time Pricing

All Retailers Analysis

offers=client.get_current_offers("012345678901")puts"Found #{offers.data.length} offers across retailers"# Advanced price analysissorted_offers=offers.data.sort_by(&:price)cheapest=sorted_offers.firstmost_expensive=sorted_offers.lastputs"💰 Best price: #{cheapest.retailer} - $#{cheapest.price}"puts"💸 Highest price: #{most_expensive.retailer} - $#{most_expensive.price}"puts"📊 Average price: $#{offers.data.map(&:price).sum / offers.data.length}"puts"💡 Potential savings: $#{most_expensive.price - cheapest.price}"# Filter by availability and conditionin_stock_offers=offers.data.select{ |offer| offer.availability == 'in_stock'}new_condition_offers=offers.data.select{ |offer| offer.condition == 'new'}puts"✅ In-stock offers: #{in_stock_offers.length}"puts"🆕 New condition: #{new_condition_offers.length}"

Retailer-Specific Queries

# Major retailersamazon_offers=client.get_current_offers("012345678901",retailer: "amazon")walmart_offers=client.get_current_offers("012345678901",retailer: "walmart")target_offers=client.get_current_offers("012345678901",retailer: "target")bestbuy_offers=client.get_current_offers("012345678901",retailer: "bestbuy")# Compare specific retailersretailers=%w[amazonwalmarttargetbestbuy]retailer_prices={}retailers.eachdo |retailer|
offers=client.get_current_offers("012345678901",retailer: retailer)ifoffers.data.any?best_offer=offers.data.min_by(&:price)retailer_prices[retailer]=best_offer.priceendendputs"Retailer price comparison:"retailer_prices.sort_by{ |_,price| price}.eachdo |retailer,price|
puts" #{retailer.capitalize}: $#{price}"end

Bulk Price Monitoring

# Monitor multiple products simultaneouslyproduct_list=["012345678901","B08N5WRWNW","045496590048","B07XJ8C8F5","B09G9FPHY6"]batch_offers=client.get_current_offers_batch(product_list)batch_offers.data.eachdo |identifier,offers|
nextifoffers.empty?best_offer=offers.min_by(&:price)puts"#{identifier}:"puts" Best price: #{best_offer.retailer} - $#{best_offer.price}"puts" Total offers: #{offers.length}"puts" In stock: #{offers.count{ |o| o.availability == 'in_stock'}}"putsend

Historical Price Analysis

Comprehensive Price Trends

require'date'# Get 90 days of price history for detailed analysisend_date=Date.todaystart_date=end_date - 90history=client.get_price_history("012345678901",start_date.strftime("%Y-%m-%d"),end_date.strftime("%Y-%m-%d"))puts"📈 90-Day Price Analysis"puts"=" * 50history.data.eachdo |offer|
nextifoffer.price_history.empty?prices=offer.price_history.map(&:price)current_price=offer.price# Statistical analysisavg_price=prices.sum.to_f / prices.lengthmin_price=prices.minmax_price=prices.max# Price trend calculationrecent_prices=prices.last(7)# Last weekolder_prices=prices.first([prices.length - 7,1].max)trend=ifrecent_prices.any? && older_prices.any?recent_avg=recent_prices.sum.to_f / recent_prices.lengtholder_avg=older_prices.sum.to_f / older_prices.lengthchange_pct=((recent_avg - older_avg) / older_avg * 100).round(1)ifchange_pct > 5"📈 Rising (+#{change_pct}%)"elsifchange_pct < -5"📉 Falling (#{change_pct}%)"else"📊 Stable (#{change_pct}%)"endelse"📊 Insufficient data"endputs"🏪 #{offer.retailer.upcase}"puts" Current: $#{current_price}"puts" Average: $#{avg_price.round(2)}"puts" Range: $#{min_price} - $#{max_price}"puts" Savings opportunity: $#{(current_price - min_price).round(2)}"puts" Trend: #{trend}"puts" Data points: #{offer.price_history.length}"putsend

Retailer-Specific Historical Analysis

# Compare price history across major retailersretailers=%w[amazonwalmarttargetbestbuy]historical_comparison={}retailers.eachdo |retailer|
history=client.get_price_history("012345678901","2024-01-01","2024-12-31",retailer: retailer)nextifhistory.data.empty?offer=history.data.firstifoffer.price_history.any?prices=offer.price_history.map(&:price)historical_comparison[retailer]={current: offer.price,average: prices.sum.to_f / prices.length,lowest: prices.min,highest: prices.max,volatility: prices.max - prices.min}endendputs"Retailer Historical Comparison:"historical_comparison.eachdo |retailer,data|
puts"#{retailer.capitalize}:"puts" Current: $#{data[:current]}"puts" Average: $#{data[:average].round(2)}"puts" Best ever: $#{data[:lowest]}"puts" Worst: $#{data[:highest]}"puts" Volatility: $#{data[:volatility].round(2)}"putsend

Product Monitoring

Schedule Monitoring

# Monitor daily across all retailersresult=client.schedule_product_monitoring("012345678901","daily")puts"Scheduled: #{result.data['scheduled']}"# Monitor hourly at Amazon onlyclient.schedule_product_monitoring("012345678901","hourly",retailer: "amazon")# Schedule multiple productsbatch_result=client.schedule_product_monitoring_batch(["012345678901","B08N5WRWNW"],"daily")

Manage Scheduled Products

# Get all scheduled productsscheduled=client.get_scheduled_productsputs"Monitoring #{scheduled.data.length} products"scheduled.data.eachdo |product|
retailer_info=product.retailer || "all retailers"puts"#{product.identifier}: #{product.frequency} at #{retailer_info}"puts" Created: #{product.created_at}"puts" Last refreshed: #{product.last_refreshed}"ifproduct.last_refreshedend# Remove from scheduleclient.remove_product_from_schedule("012345678901")# Remove multiple productsclient.remove_products_from_schedule(["012345678901","B08N5WRWNW"])

Usage Tracking

usage=client.get_usageputs"Credits remaining: #{usage.data.credits_remaining}"puts"Credits used: #{usage.data.credits_used}"puts"Plan: #{usage.data.plan_name}"puts"Usage: #{usage.data.credits_percentage_used}%"puts"Billing period: #{usage.data.billing_period_start} to #{usage.data.billing_period_end}"

🔧 Advanced Usage

Error Handling

beginproduct=client.get_product_details("invalid-identifier")rescueShopsavvyDataApi::NotFoundError=>eputs"Product not found"rescueShopsavvyDataApi::AuthenticationError=>eputs"Invalid API key"rescueShopsavvyDataApi::RateLimitError=>eputs"Rate limit exceeded - slow down requests"rescueShopsavvyDataApi::ValidationError=>eputs"Invalid request parameters: #{e.message}"rescueShopsavvyDataApi::TimeoutError=>eputs"Request timed out"rescueShopsavvyDataApi::NetworkError=>eputs"Network error: #{e.message}"rescueShopsavvyDataApi::APIError=>eputs"API error: #{e.message}"puts"Status code: #{e.status_code}"ife.status_codeend

Response Format

All API methods return a consistent response format:

response=client.get_product_details("012345678901")puts"Success: #{response.success?}"puts"Data: #{response.data}"puts"Credits used: #{response.credits_used}"puts"Credits remaining: #{response.credits_remaining}"# Access the actual dataproduct=response.dataputs"Product name: #{product.name}"

Model Convenience Methods

# Offer convenience methodsoffer=offers.data.firstputs"In stock: #{offer.in_stock?}"puts"New condition: #{offer.new_condition?}"# Scheduled product convenience methodsscheduled_product=scheduled.data.firstputs"Daily monitoring: #{scheduled_product.daily?}"# Usage info convenience methodsusage_info=usage.dataputs"#{usage_info.credits_percentage_remaining}% credits remaining"

CSV Format

Some endpoints support CSV format for easier data processing:

# Get product details in CSV formatproduct_csv=client.get_product_details("012345678901",format: "csv")# Get offers in CSV formatoffers_csv=client.get_current_offers("012345678901",format: "csv")# Process with CSV libraryrequire'csv'CSV.parse(offers_csv.data,headers: true)do |row|
puts"#{row['retailer']}: $#{row['price']}"end

Working with Hashes

All model objects can be converted to hashes:

product=client.get_product_details("012345678901")# Convert to hashproduct_hash=product.data.to_hputsproduct_hash[:name]# Convert entire response to hashresponse_hash=product.to_hputsresponse_hash[:data][:name]

🚀 Production Deployment

Ruby on Rails Integration

# Gemfilegem'shopsavvy-sdk'gem'sidekiq'# For background jobs# config/application.rbconfig.shopsavvy_api_key=Rails.application.credentials.shopsavvy_api_key# app/services/price_tracking_service.rbclassPriceTrackingServicedefinitialize@client=ShopsavvyDataApi.new(api_key: Rails.application.config.shopsavvy_api_key,timeout: 60)enddeftrack_product(product_id,target_price)# Schedule monitoring@client.schedule_product_monitoring(product_id,'daily')# Create local tracking recordPriceAlert.create!(product_identifier: product_id,target_price: target_price,status: 'active')enddefcheck_price_alertsPriceAlert.active.find_eachdo |alert|
CheckPriceAlertJob.perform_later(alert.id)endendend# app/jobs/check_price_alert_job.rbclassCheckPriceAlertJob < ApplicationJobqueue_as:defaultdefperform(alert_id)alert=PriceAlert.find(alert_id)client=ShopsavvyDataApi.new(api_key: Rails.application.config.shopsavvy_api_key)offers=client.get_current_offers(alert.product_identifier)best_offer=offers.data.min_by(&:price)ifbest_offer && best_offer.price <= alert.target_price# Send notificationPriceAlertMailer.target_reached(alert,best_offer).deliver_nowalert.update!(status: 'triggered',triggered_at: Time.current)endrescueShopsavvyDataApi::Error=>eRails.logger.error"ShopSavvy API error: #{e.message}"# Optionally retry or alert administratorsendend

Sinatra Microservice

# app.rbrequire'sinatra'require'json'require'shopsavvy_data_api'classPriceAPI < Sinatra::Baseconfiguredoset:shopsavvy_client,ShopsavvyDataApi.new(api_key: ENV['SHOPSAVVY_API_KEY'],timeout: 30)endbeforedocontent_type:jsonendget'/api/product/:identifier/price'doidentifier=params[:identifier]beginoffers=settings.shopsavvy_client.get_current_offers(identifier){success: true,product_id: identifier,offers: offers.data.mapdo |offer|
{retailer: offer.retailer,price: offer.price,availability: offer.availability,condition: offer.condition,url: offer.url}end,best_price: offers.data.min_by(&:price)&.price,credits_remaining: offers.credits_remaining}.to_jsonrescueShopsavvyDataApi::Error=>estatus400{success: false,error: e.message}.to_jsonendendget'/api/product/:identifier/history'doidentifier=params[:identifier]days=(params[:days] || 30).to_iend_date=Date.todaystart_date=end_date - daysbeginhistory=settings.shopsavvy_client.get_price_history(identifier,start_date.strftime('%Y-%m-%d'),end_date.strftime('%Y-%m-%d')){success: true,product_id: identifier,period: "#{days} days",data: history.data}.to_jsonrescueShopsavvyDataApi::Error=>estatus400{success: false,error: e.message}.to_jsonendendend

Background Processing with Sidekiq

# lib/price_monitor.rbclassPriceMonitorincludeSidekiq::Workersidekiq_optionsretry: 3,backtrace: truedefperform(product_ids)client=ShopsavvyDataApi.new(api_key: ENV['SHOPSAVVY_API_KEY'])product_ids.eachdo |product_id|
begin# Get current pricesoffers=client.get_current_offers(product_id)nextifoffers.data.empty?# Store in database or cachebest_price=offers.data.min_by(&:price).priceRedis.current.setex("price:#{product_id}",3600,best_price)# Check for alertscheck_price_alerts(product_id,best_price)rescueShopsavvyDataApi::RateLimitError=>e# Exponential backoffself.class.perform_in(2 ** sidekiq_options['retry_count'],[product_id])raiseerescueShopsavvyDataApi::Error=>elogger.error"API error for #{product_id}: #{e.message}"endendendprivatedefcheck_price_alerts(product_id,current_price)# Implementation for checking and triggering alertsendend# Schedule regular monitoringPriceMonitor.perform_async(['012345678901','B08N5WRWNW'])

💡 Real-World Use Cases

E-commerce Price Intelligence

# Comprehensive competitive analysis toolclassCompetitiveAnalyzerdefinitialize(api_key)@client=ShopsavvyDataApi.new(api_key: api_key)enddefanalyze_market(product_ids,competitors=%w[amazonwalmarttargetbestbuy])analysis={}product_ids.eachdo |product_id|
product_analysis=analyze_product_competition(product_id,competitors)analysis[product_id]=product_analysisendgenerate_competitive_report(analysis)endprivatedefanalyze_product_competition(product_id,competitors)# Get product detailsproduct=@client.get_product_details(product_id)# Get current offers from all retailersall_offers=@client.get_current_offers(product_id)# Filter by target competitorscompetitor_offers=all_offers.data.selectdo |offer|
competitors.include?(offer.retailer.downcase)end# Price analysisprices=competitor_offers.map(&:price){product_name: product.data.name,brand: product.data.brand,total_offers: all_offers.data.length,competitor_offers: competitor_offers.length,price_range: {min: prices.min,max: prices.max,average: prices.sum.to_f / prices.length},market_position: calculate_market_position(competitor_offers),availability_score: calculate_availability_score(competitor_offers)}enddefcalculate_market_position(offers)return'No data'ifoffers.empty?prices=offers.map(&:price).sortmedian_price=prices[prices.length / 2]casemedian_pricewhen0..50then'Budget'when50..200then'Mid-range'when200..500then'Premium'else'Luxury'endenddefcalculate_availability_score(offers)return0ifoffers.empty?in_stock_count=offers.count{ |offer| offer.availability == 'in_stock'}(in_stock_count.to_f / offers.length * 100).round(1)endend# Usageanalyzer=CompetitiveAnalyzer.new(ENV['SHOPSAVVY_API_KEY'])report=analyzer.analyze_market(['012345678901','B08N5WRWNW','045496590048'])

Inventory Management Integration

# Integration with inventory management systemclassInventoryPriceManagerdefinitialize(api_key)@client=ShopsavvyDataApi.new(api_key: api_key)enddefupdate_competitive_pricing(inventory_items)pricing_updates=[]inventory_items.eachdo |item|
nextunlessitem.competitor_tracking_enabled?begin# Get current market pricesoffers=@client.get_current_offers(item.barcode)nextifoffers.data.empty?# Calculate competitive price pointcompetitor_prices=offers.data.map(&:price)market_analysis=analyze_market_prices(competitor_prices)suggested_price=calculate_competitive_price(item.cost_price,market_analysis,item.target_margin)pricing_updates << {item_id: item.id,current_price: item.selling_price,suggested_price: suggested_price,market_analysis: market_analysis,reasoning: generate_pricing_reasoning(item,market_analysis,suggested_price)}rescueShopsavvyDataApi::Error=>eRails.logger.error"Pricing update failed for #{item.id}: #{e.message}"nextendendpricing_updatesendprivatedefanalyze_market_prices(prices)sorted_prices=prices.sort{min: sorted_prices.first,max: sorted_prices.last,median: sorted_prices[sorted_prices.length / 2],average: prices.sum.to_f / prices.length,percentile_25: sorted_prices[(sorted_prices.length * 0.25).round],percentile_75: sorted_prices[(sorted_prices.length * 0.75).round]}enddefcalculate_competitive_price(cost_price,market_analysis,target_margin)min_price=cost_price * (1 + target_margin)competitive_price=market_analysis[:percentile_25] * 0.95# 5% under 25th percentile[min_price,competitive_price].max.round(2)endend

Market Research Analytics

# Advanced market research and trend analysisclassMarketResearcherdefinitialize(api_key)@client=ShopsavvyDataApi.new(api_key: api_key)enddefresearch_category_trends(product_categories,time_periods)research_report={}product_categories.eachdo |category,product_list|
category_data=analyze_category_trends(product_list,time_periods)research_report[category]=category_dataendgenerate_market_intelligence_report(research_report)enddeftrack_seasonal_patterns(product_id,months=12)patterns={}(0...months).eachdo |month_offset|
end_date=Date.today - (month_offset * 30)start_date=end_date - 30history=@client.get_price_history(product_id,start_date.strftime('%Y-%m-%d'),end_date.strftime('%Y-%m-%d'))month_name=end_date.strftime('%B %Y')patterns[month_name]=analyze_monthly_patterns(history.data)endidentify_seasonal_trends(patterns)endprivatedefanalyze_monthly_patterns(history_data)return{average_price: 0,volatility: 0}ifhistory_data.empty?all_prices=[]history_data.eachdo |offer|
nextifoffer.price_history.empty?all_prices.concat(offer.price_history.map(&:price))endreturn{average_price: 0,volatility: 0}ifall_prices.empty?average=all_prices.sum.to_f / all_prices.lengthvariance=all_prices.map{ |price| (price - average) ** 2}.sum / all_prices.length{average_price: average.round(2),volatility: Math.sqrt(variance).round(2),price_points: all_prices.length}endend

🛠️ Development & Testing

Local Development Setup

# Clone the repositorygitclonehttps://github.com/shopsavvy/sdk-ruby.gitcdsdk-ruby# Install dependenciesbundleinstall# Set up environment variablesecho'SHOPSAVVY_API_KEY=ss_test_your_test_key_here' > .env# Run testsbundleexecrspec# Run lintingbundleexecrubocop# Generate documentationbundleexecyarddoc

Testing Your Integration

# Create a test scriptrequire'shopsavvy_data_api'# Use test API key (starts with ss_test_)client=ShopsavvyDataApi.new(api_key: 'ss_test_your_test_key_here')# Test basic functionalitybegin# Test product lookupproduct=client.get_product_details('012345678901')puts"✅ Product lookup: #{product.data.name}"# Test current offersoffers=client.get_current_offers('012345678901')puts"✅ Current offers: #{offers.data.length} found"# Test usage infousage=client.get_usageputs"✅ API usage: #{usage.data.credits_remaining} credits remaining"puts"\n🎉 All tests passed! SDK is working correctly."rescueShopsavvyDataApi::Error=>eputs"❌ Test failed: #{e.message}"end

📚 Additional Resources

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details on:

  • Reporting bugs
  • Suggesting enhancements
  • Submitting pull requests
  • Development workflow
  • Code standards

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🏢 About ShopSavvy

ShopSavvy is the world's first mobile shopping app, helping consumers find the best deals since 2008. With over 40 million downloads and millions of active users, ShopSavvy has saved consumers billions of dollars.

Our Data API Powers:

  • 🛒 E-commerce platforms with competitive intelligence
  • 📊 Market research with real-time pricing data
  • 🏪 Retailers with inventory and pricing optimization
  • 📱 Mobile apps with product lookup and price comparison
  • 🤖 Business intelligence with automated price monitoring

Why Choose ShopSavvy Data API?

  • Trusted by millions - Proven at scale since 2008
  • Comprehensive coverage - 1000+ retailers, millions of products
  • Real-time accuracy - Fresh data updated continuously
  • Developer-friendly - Easy integration, great documentation
  • Reliable infrastructure - 99.9% uptime, enterprise-grade
  • Flexible pricing - Plans for every use case and budget

Ready to get started?Sign up for your API keyNeed help?Contact us

About

Official Ruby SDK for ShopSavvy Data API

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages