Skip to content

About

Official C#/.NET SDK for the ShopSavvy Data API: look up products by barcode (UPC/EAN/ISBN), ASIN, URL, model number or MPN, and get current retailer offers and price history.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

ShopSavvy Data API - C# SDK

NuGet Version .NET Standard License: MIT Documentation

Official C# SDK for the ShopSavvy Data API. Access comprehensive product data, real-time pricing, and historical price trends across tens of thousands of retailers and 30M+ products.

⚑ 30-Second Quick Start

// Install
// dotnet add package ShopSavvy.Sdk

// Use
using ShopSavvy.DataApi;

var client = new ShopSavvyDataApiClient("ss_live_your_api_key_here");
var product = await client.GetProductDetailsAsync("012345678901");
var offers = await client.GetOffersAsync("012345678901");   // one entry per product, each with Offers
var bestPrice = offers.Data[0].Offers.Where(o => o.Price.HasValue).MinBy(o => o.Price);
Console.WriteLine($"{product.Data.Title} - Best price: ${bestPrice.Price:F2} at {bestPrice.Retailer}");

πŸš€ Installation & Setup

NuGet Package Manager

Install-Package ShopSavvy.Sdk

.NET CLI

dotnet add package ShopSavvy.Sdk

PackageReference

<PackageReference Include="ShopSavvy.Sdk" Version="1.4.0" />

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

πŸ“– Complete API Reference

Client Configuration

// Basic configuration
using var client = new ShopSavvyDataApiClient("ss_live_your_api_key_here");

// Advanced configuration
var config = new ShopSavvyConfig
{
    ApiKey = "ss_live_your_api_key_here",
    BaseUrl = "https://api.shopsavvy.com/v1",  // Custom base URL
    Timeout = TimeSpan.FromSeconds(60),        // Request timeout
    RetryAttempts = 3,                         // Retry failed requests
    RetryDelay = TimeSpan.FromSeconds(1),      // Delay between retries
    UserAgent = "MyApp/1.0.0"                  // Custom user agent
};

using var client = new ShopSavvyDataApiClient(config);

// Environment variable configuration
var apiKey = Environment.GetEnvironmentVariable("SHOPSAVVY_API_KEY");
using var client = new ShopSavvyDataApiClient(apiKey);

Product Lookup

Single Product

// Look up by barcode, ASIN, URL, model number, or ShopSavvy ID
var product = await client.GetProductDetailsAsync("012345678901");
var amazonProduct = await client.GetProductDetailsAsync("B08N5WRWNW");
var urlProduct = await client.GetProductDetailsAsync("https://www.amazon.com/dp/B08N5WRWNW");
var modelProduct = await client.GetProductDetailsAsync("MQ023LL/A");  // iPhone model number

Console.WriteLine($"πŸ“¦ Product: {product.Data.Name}");
Console.WriteLine($"🏷️ Brand: {product.Data.Brand}");
Console.WriteLine($"πŸ“‚ Category: {product.Data.Category}");
Console.WriteLine($"πŸ”’ Product ID: {product.Data.ProductId}");

if (!string.IsNullOrEmpty(product.Data.Asin))
    Console.WriteLine($"πŸ“¦ ASIN: {product.Data.Asin}");

if (!string.IsNullOrEmpty(product.Data.Model))
    Console.WriteLine($"πŸ”§ Model: {product.Data.Model}");

Bulk Product Lookup

// Up to 100 identifiers per request
var identifiers = new[]
{
    "012345678901", "B08N5WRWNW", "045496590048",
    "https://www.bestbuy.com/site/product/123456",
    "MQ023LL/A", "SM-S911U"  // iPhone and Samsung model numbers
};

var products = await client.GetProductDetailsBatchAsync(identifiers);

foreach (var product in products.Data)
{
    if (product != null)
    {
        Console.WriteLine($"βœ“ Found: {product.Name} by {product.Brand}");
        if (product.Identifiers?.Any() == true)
        {
            Console.WriteLine($"  Identifiers: {string.Join(", ", product.Identifiers.Select(i => $"{i.Key}:{i.Value}"))}");
        }
    }
}

// Handle potential errors in batch processing
for (int i = 0; i < products.Data.Length; i++)
{
    if (products.Data[i] == null)
    {
        Console.WriteLine($"❌ Failed to find product: {identifiers[i]}");
    }
}

Real-Time Pricing

All Retailers Analysis

// GetOffersAsync returns one entry per matched product: every product field plus its Offers
var response = await client.GetOffersAsync("012345678901");
var product = response.Data[0];
var offers = product.Offers.Where(o => o.Price.HasValue).ToArray();
Console.WriteLine($"{product.Title}: {offers.Length} offers across retailers");

// Advanced price analysis
var sortedOffers = offers.OrderBy(o => o.Price).ToArray();
var cheapest = sortedOffers.First();
var mostExpensive = sortedOffers.Last();

Console.WriteLine($"πŸ’° Best price: {cheapest.Retailer} - ${cheapest.Price:F2}");
Console.WriteLine($"πŸ’Έ Highest price: {mostExpensive.Retailer} - ${mostExpensive.Price:F2}");
Console.WriteLine($"πŸ“Š Average price: ${offers.Average(o => o.Price):F2}");
Console.WriteLine($"πŸ’‘ Potential savings: ${mostExpensive.Price - cheapest.Price:F2}");

// Filter by availability ("in", "out", "limited", "pre-order", "coming-soon", "discontinued";
// null when unknown) and condition
var inStockOffers = offers.Where(o => o.Availability == "in").ToArray();
var newConditionOffers = offers.Where(o => o.Condition == "new").ToArray();

Console.WriteLine($"βœ… In-stock offers: {inStockOffers.Length}");
Console.WriteLine($"πŸ†• New condition: {newConditionOffers.Length}");

Retailer-Specific Queries

// Major retailers (retailer = domain name)
var retailers = new[] { "amazon.com", "walmart.com", "target.com", "bestbuy.com" };
var retailerPrices = new Dictionary<string, decimal>();

foreach (var retailer in retailers)
{
    var response = await client.GetOffersAsync("012345678901", retailer);
    var offers = response.Data.SelectMany(p => p.Offers).Where(o => o.Price.HasValue).ToArray();
    if (offers.Any())
    {
        retailerPrices[retailer] = offers.Min(o => o.Price!.Value);
    }
}

Console.WriteLine("Retailer price comparison:");
foreach (var (retailer, price) in retailerPrices.OrderBy(kvp => kvp.Value))
{
    Console.WriteLine($"  {char.ToUpper(retailer[0])}{retailer[1..]}: ${price:F2}");
}

Bulk Price Monitoring

// Monitor multiple products simultaneously
var productList = new[]
{
    "012345678901", "B08N5WRWNW", "045496590048",
    "B07XJ8C8F5", "B09G9FPHY6"
};

var batchOffers = await client.GetOffersBatchAsync(productList);

// One entry per identifier that matched a product (unknown identifiers are omitted)
foreach (var product in batchOffers.Data)
{
    var offers = product.Offers.Where(o => o.Price.HasValue).ToArray();
    if (!offers.Any()) continue;
    
    var bestOffer = offers.MinBy(o => o.Price);
    Console.WriteLine($"{product.Title} ({product.Barcode}):");
    Console.WriteLine($"  Best price: {bestOffer.Retailer} - ${bestOffer.Price:F2}");
    Console.WriteLine($"  Total offers: {offers.Length}");
    Console.WriteLine($"  In stock: {offers.Count(o => o.Availability == "in")}");
    Console.WriteLine();
}

Historical Price Analysis

Comprehensive Price Trends

GetPriceHistoryAsync returns ApiResponse<ProductWithPriceHistory[]>: one entry per matched product (every ProductDetails field, plus Offers). Each OfferWithHistory carries the offer's current state at that retailer and a History array of PriceHistoryEntry points (Timestamp, Price, Currency, Availability), newest first. Currency can be null and Availability is null when it was not confirmed at that point. The window may span at most 366 days.

// Get 90 days of price history for detailed analysis
var endDate = DateTime.Today;
var startDate = endDate.AddDays(-90);

var history = await client.GetPriceHistoryAsync(
    "012345678901",
    startDate.ToString("yyyy-MM-dd"),
    endDate.ToString("yyyy-MM-dd")
);

foreach (var product in history.Data)
{
    Console.WriteLine($"πŸ“ˆ 90-Day Price Analysis: {product.Title}");
    Console.WriteLine(new string('=', 50));

    foreach (var offer in product.Offers.Where(o => o.History.Length > 0))
    {
        var prices = offer.History.Select(point => point.Price).ToArray();
        var currentPrice = offer.Price ?? prices[0];

        // Points are newest first: the first 7 are the most recent ones
        var recentPrices = prices.Take(7).ToArray();
        var olderPrices = prices.Skip(7).ToArray();

        var trend = "πŸ“Š Insufficient data";
        if (recentPrices.Any() && olderPrices.Any())
        {
            var recentAvg = recentPrices.Average();
            var olderAvg = olderPrices.Average();
            var changePct = Math.Round((recentAvg - olderAvg) / olderAvg * 100, 1);

            trend = changePct switch
            {
                > 5 => $"πŸ“ˆ Rising (+{changePct}%)",
                < -5 => $"πŸ“‰ Falling ({changePct}%)",
                _ => $"πŸ“Š Stable ({changePct:+0.0;-0.0;0.0}%)"
            };
        }

        Console.WriteLine($"πŸͺ {offer.Retailer}");
        Console.WriteLine($"  Current: {currentPrice:F2} {offer.Currency}");
        Console.WriteLine($"  Average: {prices.Average():F2}");
        Console.WriteLine($"  Range: {prices.Min():F2} - {prices.Max():F2}");
        Console.WriteLine($"  Trend: {trend}");
        Console.WriteLine($"  Data points: {offer.History.Length} (latest {offer.History[0].Timestamp})");
        Console.WriteLine();
    }
}

Retailer-Specific Historical Analysis

// Compare price history across major retailers (retailer = domain name)
var retailers = new[] { "amazon.com", "walmart.com", "target.com", "bestbuy.com" };
var historicalComparison = new Dictionary<string, dynamic>();

foreach (var retailer in retailers)
{
    var history = await client.GetPriceHistoryAsync(
        "012345678901",
        "2024-01-01",
        "2024-12-31",
        retailer
    );

    var product = history.Data.FirstOrDefault();
    var offer = product?.Offers.FirstOrDefault(o => o.History.Length > 0);
    if (offer == null) continue;

    var prices = offer.History.Select(point => point.Price).ToArray();
    historicalComparison[retailer] = new
    {
        Current = offer.Price ?? prices[0],
        Average = prices.Average(),
        Lowest = prices.Min(),
        Highest = prices.Max(),
        Volatility = prices.Max() - prices.Min()
    };
}

Console.WriteLine("Retailer Historical Comparison:");
foreach (var (retailer, data) in historicalComparison)
{
    Console.WriteLine($"{char.ToUpper(retailer[0])}{retailer[1..]}:");
    Console.WriteLine($"  Current: ${data.Current:F2}");
    Console.WriteLine($"  Average: ${data.Average:F2}");
    Console.WriteLine($"  Best ever: ${data.Lowest:F2}");
    Console.WriteLine($"  Worst: ${data.Highest:F2}");
    Console.WriteLine($"  Volatility: ${data.Volatility:F2}");
    Console.WriteLine();
}

Scheduled Refresh Monitoring

Schedule products to be refreshed hourly, daily or weekly (credit costs are in the API documentation). The schedule and list calls return ScheduledProduct entries β€” every product field (Title, Shopsavvy, Barcode, …) plus Schedule and Retailer. Retailer is null when a product is watched across all retailers; on the list, Schedule is null for an interval with no Data API label. Unscheduling is free and returns only Success, Message and Meta β€” no per-product data.

// Schedule one product daily at a single retailer
var scheduled = await client.ScheduleProductMonitoringAsync("012345678901", "daily", "bestbuy.com");
foreach (var p in scheduled.Data) // data is always a list; unknown identifiers are omitted
{
    Console.WriteLine($"{p.Title} ({p.Shopsavvy}) -> {p.Schedule} at {p.Retailer ?? "all retailers"}");
}
Console.WriteLine($"Credits used: {scheduled.CreditsUsed()}");

// Schedule several products hourly across all retailers
var batch = await client.ScheduleProductMonitoringBatchAsync(new[] { "012345678901", "B08N5WRWNW" }, "hourly");

// List everything on the schedule
var list = await client.GetScheduledProductsAsync();
foreach (var p in list.Data)
{
    Console.WriteLine($"{p.Title}: {p.Schedule ?? "custom interval"}{(p.Retailer != null ? $" @ {p.Retailer}" : "")}");
}

// Unschedule (single or batch) β€” no data, just a confirmation
var removed = await client.RemoveProductsFromScheduleAsync(new[] { "012345678901", "B08N5WRWNW" });
Console.WriteLine($"{removed.Success}: {removed.Message}");

πŸš€ Production Deployment

ASP.NET Core Integration

// Program.cs or Startup.cs
builder.Services.Configure<ShopSavvyConfig>(
    builder.Configuration.GetSection("ShopSavvy"));

builder.Services.AddScoped<ShopSavvyDataApiClient>(provider =>
{
    var config = provider.GetRequiredService<IOptions<ShopSavvyConfig>>().Value;
    return new ShopSavvyDataApiClient(config);
});

// appsettings.json
{
  "ShopSavvy": {
    "ApiKey": "ss_live_your_api_key_here",
    "Timeout": "00:01:00",
    "RetryAttempts": 3
  }
}

// PriceController.cs
[ApiController]
[Route("api/[controller]")]
public class PriceController : ControllerBase
{
    private readonly ShopSavvyDataApiClient _client;
    
    public PriceController(ShopSavvyDataApiClient client)
    {
        _client = client;
    }
    
    [HttpGet("{identifier}/offers")]
    public async Task<IActionResult> GetOffers(string identifier)
    {
        try
        {
            var response = await _client.GetOffersAsync(identifier);
            return Ok(new
            {
                success = true,
                productId = identifier,
                offers = response.Data.SelectMany(p => p.Offers).Select(o => new
                {
                    retailer = o.Retailer,
                    price = o.Price,
                    availability = o.Availability,
                    condition = o.Condition,
                    url = o.Url
                }),
                bestPrice = response.Data.SelectMany(p => p.Offers).Min(o => o.Price),
                creditsRemaining = response.CreditsRemaining()
            });
        }
        catch (ShopSavvyApiException ex)
        {
            return BadRequest(new { success = false, error = ex.Message });
        }
    }
}

Background Services

// PriceMonitoringService.cs
public class PriceMonitoringService : BackgroundService
{
    private readonly ShopSavvyDataApiClient _client;
    private readonly ILogger<PriceMonitoringService> _logger;
    
    public PriceMonitoringService(
        ShopSavvyDataApiClient client,
        ILogger<PriceMonitoringService> logger)
    {
        _client = client;
        _logger = logger;
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                await MonitorPrices();
                await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error in price monitoring service");
            }
        }
    }
    
    private async Task MonitorPrices()
    {
        var products = new[] { "012345678901", "B08N5WRWNW" };
        
        foreach (var productId in products)
        {
            try
            {
                var response = await _client.GetOffersAsync(productId);
                var bestPrice = response.Data.SelectMany(p => p.Offers).Min(o => o.Price);
                
                // Store in database or send alerts
                _logger.LogInformation($"Product {productId}: Best price ${bestPrice:F2}");
            }
            catch (ShopSavvyRateLimitException)
            {
                // Implement exponential backoff
                await Task.Delay(TimeSpan.FromMinutes(5));
            }
            catch (ShopSavvyApiException ex)
            {
                _logger.LogError(ex, $"API error for product {productId}");
            }
        }
    }
}

πŸ› οΈ Development & Testing

Local Development Setup

# Clone the repository
git clone https://github.com/shopsavvy/sdk-csharp.git
cd sdk-csharp

# Restore packages
dotnet restore

# Build the project
dotnet build

# Run tests
dotnet test tests/ShopSavvy.DataApi.Tests

# Create NuGet package
dotnet pack

Testing Your Integration

using ShopSavvy.DataApi;

// Use test API key (starts with ss_test_)
using var client = new ShopSavvyDataApiClient("ss_test_your_test_key_here");

try
{
    // Test product lookup
    var product = await client.GetProductDetailsAsync("012345678901");
    Console.WriteLine($"βœ… Product lookup: {product.Data.Name}");
    
    // Test current offers
    var offers = await client.GetOffersAsync("012345678901");
    Console.WriteLine($"βœ… Current offers: {offers.Data.Sum(p => p.Offers.Length)} found");
    
    // Test usage info
    var usage = await client.GetUsageAsync();
    Console.WriteLine($"βœ… API usage: {usage.Data.CreditsRemaining} credits remaining");
    
    Console.WriteLine("\nπŸŽ‰ All tests passed! SDK is working correctly.");
}
catch (ShopSavvyApiException ex)
{
    Console.WriteLine($"❌ Test failed: {ex.Message}");
}

πŸ“š 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 has been comparing prices since 2008, and the app has 40M+ downloads. The ShopSavvy Data API serves the same product and price data over REST: send a barcode (UPC/EAN/ISBN), ASIN, product URL, model number or MPN, and get back the product record, current offers from tens of thousands of retailers, and price history back to 2011 (dense from 2019).

  • Lookup by any identifier: barcode, ASIN, URL, model number, MPN or ShopSavvy ID, up to 100 per request
  • Current offers: price, availability, condition, seller and when each offer was last seen
  • Price history: quality-filtered price points per retailer over any date range
  • Scheduled refreshes: hourly, daily or weekly re-checks for the products you care about, at retailers that can be re-checked through an official retailer API (others update as ShopSavvy sees them)
  • JSON or CSV responses, with credits used and remaining on every response
  • Open-source SDKs in 10 languages: Python, TypeScript, Go, Rust, Java, Kotlin, Swift, PHP, Ruby and C#

Plans and the 14-day trial are at shopsavvy.com/data; full reference at shopsavvy.com/data/documentation.


Ready to get started? Sign up for your API key β€’ Need help? Contact us

About

Official C#/.NET SDK for the ShopSavvy Data API: look up products by barcode (UPC/EAN/ISBN), ASIN, URL, model number or MPN, and get current retailer offers and price history.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages