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.
# 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}"Add to your Gemfile:
gem'shopsavvy-sdk'Or install directly:
gem install shopsavvy-sdk- Sign up: Visit shopsavvy.com/data
- Choose plan: Select based on your usage needs
- Get API key: Copy from your dashboard
- Test: Run the 30-second example above
# For production, use environment variablesENV['SHOPSAVVY_API_KEY']='ss_live_your_api_key_here'# Initialize clientclient=ShopsavvyDataApi.new(api_key: ENV['SHOPSAVVY_API_KEY'])# 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)# 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# 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}"endendoffers=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}"# 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# 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'}}"putsendrequire'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# 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# 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")# 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=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}"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_codeendAll 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}"# 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"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']}"endAll 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]# 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# 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# 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'])# 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'])# 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# 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# 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# 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- ShopSavvy Data API Documentation - Complete API reference
- API Dashboard - Manage your API keys and usage
- GitHub Repository - Source code and issues
- RubyGems Page - Gem releases and stats
- Support - Get help from our team
We welcome contributions! Please see our Contributing Guide for details on:
- Reporting bugs
- Suggesting enhancements
- Submitting pull requests
- Development workflow
- Code standards
This project is licensed under the MIT License - see the LICENSE file for details.
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.
- 🛒 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
- ✅ 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 key • Need help?Contact us