{"id":22128891,"url":"https://github.com/rootasjey/unsplasharp","last_synced_at":"2025-07-25T18:31:53.080Z","repository":{"id":23791192,"uuid":"99035990","full_name":"rootasjey/unsplasharp","owner":"rootasjey","description":"C# API wrapper around Unsplash 📷","archived":false,"fork":false,"pushed_at":"2022-12-07T19:29:56.000Z","size":57,"stargazers_count":42,"open_issues_count":5,"forks_count":16,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-11-04T15:05:59.381Z","etag":null,"topics":["api","api-wrapper","photos","unsplash","unsplash-api"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rootasjey.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-08-01T19:29:13.000Z","updated_at":"2024-04-26T05:15:25.000Z","dependencies_parsed_at":"2023-01-13T23:52:54.825Z","dependency_job_id":null,"html_url":"https://github.com/rootasjey/unsplasharp","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rootasjey%2Funsplasharp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rootasjey%2Funsplasharp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rootasjey%2Funsplasharp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rootasjey%2Funsplasharp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rootasjey","download_url":"https://codeload.github.com/rootasjey/unsplasharp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227606488,"owners_count":17792795,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","api-wrapper","photos","unsplash","unsplash-api"],"created_at":"2024-12-01T17:56:41.993Z","updated_at":"2025-07-25T18:31:53.060Z","avatar_url":"https://github.com/rootasjey.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\n\u003cp align=\"center\"\u003e\n  \u003ch1 align=\"center\"\u003eUnsplasharp\u003c/h1\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/rootasjey/unsplasharp\"\u003e\n    \u003cimg src=\"./icon.png\" alt=\"Unsplasharp\" width=\"200\" height=\"200\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n[![NuGet](https://img.shields.io/nuget/v/unsplasharp.api.svg)](https://www.nuget.org/packages/unsplasharp.api/)\n[![NuGet](https://img.shields.io/nuget/dt/unsplasharp.api.svg)](https://www.nuget.org/packages/unsplasharp.api/)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/rootasjey/unsplasharp/blob/master/LICENSE)\n\nUnofficial C# wrapper around [Unsplash](https://unsplash.com) API targeting .NET Standard 2.0.\n\nThis lib is compatible with .NET Core, .NET Framework 4.6.1+, Xamarin (iOS, Android), Universal Windows Platform.\n\n## ✨ Recent Improvements\n\nThis library has been enhanced with modern .NET practices and reliability features:\n\n- **🔄 Async/Await Patterns**: Improved async patterns with proper `ConfigureAwait(false)` usage\n- **⏹️ Cancellation Token Support**: All async methods now support `CancellationToken` for request cancellation and timeouts\n- **📊 Structured Logging**: Built-in support for Microsoft.Extensions.Logging with detailed request/response logging\n- **🔁 Retry Policies**: Automatic retry logic using Polly for resilient HTTP requests\n- **⚡ Performance**: Migrated to System.Text.Json for 2-3x faster JSON processing and reduced memory usage\n- **🛡️ Comprehensive Error Handling**: Structured exceptions with rich context for better debugging and error recovery\n- **🚀 Modern JSON**: System.Text.Json integration with custom converters and safe property access\n\n**Currently incomplete** 🚧\n\n* Missing [user's authentication actions](https://unsplash.com/documentation#user-authentication)\n\n## 📖 Documentation\n\nComprehensive documentation is available with guides for all experience levels:\n\n- **[📚 Complete Documentation Hub](docs/index.md)** - Start here for everything\n- **[🚀 Getting Started Guide](docs/docs/getting-started.md)** - Step-by-step setup and examples\n- **[🔍 API Reference](docs/api-reference.md)** - Complete method documentation\n- **[💡 Code Examples](docs/code-examples.md)** - Practical examples and recipes\n- **[⚡ Quick Reference](docs/quick-reference.md)** - Fast reference for common operations\n\n### Quick Navigation\n- **New to Unsplasharp?** → [Getting Started](docs/docs/getting-started.md)\n- **Need specific examples?** → [Code Examples](docs/code-examples.md)\n- **Having issues?** → [Error Handling Guide](docs/error-handling.md)\n- **Building for production?** → [Best Practices](docs/testing-best-practices.md)\n- **Upgrading versions?** → [Migration Guide](docs/migration-guide.md)\n\n## Installation\n\n[NuGet](https://www.nuget.org/packages/unsplasharp.api/): ```Install-Package unsplasharp.api```\n\n## Usage\n\n### Basic Usage\n\n```csharp\nusing Unsplasharp;\n\nvar client = new UnsplasharpClient(\"YOUR_APPLICATION_ID\");\nvar photosFound = await client.SearchPhotos(\"mountains\");\n```\n\n### Cancellation Token Support\n\nAll async methods now support `CancellationToken` for request cancellation and timeout handling:\n\n```csharp\nusing Unsplasharp;\n\nvar client = new UnsplasharpClient(\"YOUR_APPLICATION_ID\");\n\n// Timeout after 30 seconds\nusing var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));\nvar photo = await client.GetRandomPhoto(cts.Token);\n\n// Manual cancellation\nusing var cts2 = new CancellationTokenSource();\nvar searchTask = client.SearchPhotos(\"nature\", 1, 10, cts2.Token);\n// Later: cts2.Cancel();\n\n// Integration with ASP.NET Core\npublic async Task\u003cIActionResult\u003e GetPhoto(string id, CancellationToken cancellationToken)\n{\n    var photo = await client.GetPhoto(id, 0, 0, cancellationToken);\n    return Ok(photo);\n}\n```\n\n**Supported Methods:**\n- All photo methods: `GetPhoto()`, `GetRandomPhoto()`, `ListPhotos()`, `SearchPhotos()`\n- All collection methods: `GetCollection()`, `ListCollections()`, `GetCollectionPhotos()`\n- All user methods: `GetUser()`\n- All search methods: `SearchPhotos()`, `SearchCollections()`\n- Stats methods: `GetTotalStats()`\n\n\u003e **🔄 Backward Compatibility**: All existing method signatures remain unchanged. Cancellation token support is added as optional overloads, so your existing code will continue to work without any modifications.\n\n### Advanced Usage with Logging\n\n```csharp\nusing Unsplasharp;\nusing Microsoft.Extensions.Logging;\n\n// Create a logger factory\nusing var loggerFactory = LoggerFactory.Create(builder =\u003e\n    builder.AddConsole().SetMinimumLevel(LogLevel.Debug));\n\nvar logger = loggerFactory.CreateLogger\u003cUnsplasharpClient\u003e();\n\n// Create client with logging support\nvar client = new UnsplasharpClient(\"YOUR_APPLICATION_ID\", logger: logger);\n\n// The client will now log HTTP requests, retries, and errors\nvar photos = await client.SearchPhotos(\"nature\");\n```\n\n### Modern Usage with IHttpClientFactory (Recommended)\n\nFor ASP.NET Core and modern .NET applications, use the IHttpClientFactory integration:\n\n```csharp\nusing Unsplasharp.Extensions;\n\n// In Program.cs or Startup.cs\nbuilder.Services.AddUnsplasharp(\"YOUR_APPLICATION_ID\");\n\n// Or with configuration\nbuilder.Services.AddUnsplasharp(options =\u003e\n{\n    options.ApplicationId = \"YOUR_APPLICATION_ID\";\n    options.Secret = \"YOUR_SECRET\"; // Optional\n    options.ConfigureHttpClient = client =\u003e\n    {\n        client.Timeout = TimeSpan.FromSeconds(60);\n    };\n});\n\n// In your controller or service\npublic class PhotoService\n{\n    private readonly UnsplasharpClient _client;\n\n    public PhotoService(UnsplasharpClient client)\n    {\n        _client = client;\n    }\n\n    public async Task\u003cList\u003cPhoto\u003e\u003e GetPhotos(string query)\n    {\n        return await _client.SearchPhotos(query);\n    }\n}\n```\n\n### Features\n\n- **🏭 IHttpClientFactory Support**: Modern HTTP client management with proper lifecycle and DI integration\n- **🔄 Automatic Retries**: Failed requests are automatically retried up to 3 times with exponential backoff\n- **📊 Structured Logging**: Detailed logging of HTTP requests, responses, and retry attempts\n- **📈 Rate Limit Tracking**: Built-in rate limit monitoring and logging\n- **⚡ Async/Await**: All methods use proper async patterns with `ConfigureAwait(false)`\n- **🚀 High Performance JSON**: System.Text.Json for faster parsing and lower memory usage\n- **🔧 Dependency Injection**: Seamless integration with .NET DI containers\n- **🔙 Backward Compatible**: Existing code continues to work without changes\n\n### Comprehensive Error Handling\n\nUnsplasharp now provides structured exception handling with rich context information for better debugging and error recovery:\n\n```csharp\ntry {\n    // New methods that throw specific exceptions\n    var photo = await client.GetRandomPhotoAsync();\n    var specificPhoto = await client.GetPhotoAsync(\"photo-id\");\n} catch (UnsplasharpNotFoundException ex) {\n    // Handle resource not found (404)\n    Console.WriteLine($\"Photo '{ex.ResourceId}' not found\");\n} catch (UnsplasharpRateLimitException ex) {\n    // Handle rate limiting (429)\n    Console.WriteLine($\"Rate limit exceeded. Reset at: {ex.RateLimitReset}\");\n    await Task.Delay(ex.TimeUntilReset ?? TimeSpan.FromMinutes(1));\n} catch (UnsplasharpAuthenticationException ex) {\n    // Handle authentication errors (401)\n    Console.WriteLine(\"Invalid application ID or access token\");\n} catch (UnsplasharpNetworkException ex) when (ex.IsRetryable) {\n    // Handle retryable network errors\n    Console.WriteLine(\"Temporary network error, will retry automatically\");\n} catch (UnsplasharpException ex) {\n    // Handle any other API errors\n    Console.WriteLine($\"API Error: {ex.Message}\");\n    Console.WriteLine($\"Context: {ex.Context?.ToSummary()}\");\n}\n```\n\n**Key Features:**\n- **🎯 Specific Exception Types**: Different exceptions for different error scenarios\n- **📋 Rich Error Context**: Correlation IDs, timing, rate limits, and request details\n- **🔄 Intelligent Retries**: Automatic retry logic based on error types\n- **🔙 Backward Compatibility**: Existing methods still return null/empty on errors\n- **📊 Enhanced Logging**: Structured logging with correlation IDs for debugging\n\nFor detailed information, see the **[Error Handling Guide](ERROR_HANDLING.md)**.\n\n## Documentation\n\n- **[Error Handling Guide](ERROR_HANDLING.md)** - Comprehensive error handling with structured exceptions\n- **[IHttpClientFactory Integration Guide](HTTPCLIENTFACTORY.md)** - Comprehensive guide for modern .NET applications\n- **[Logging Guide](LOGGING.md)** - Detailed logging configuration and usage\n- **[System.Text.Json Migration Guide](SYSTEMTEXTJSON.md)** - Performance improvements and technical details\n- **[Changelog](CHANGELOG.md)** - Version history and breaking changes\n\n## API documentation\n\n[Official API documentation](https://unsplash.com/documentation)\n\n* [General](https://github.com/rootasjey/unsplasharp#general)\n* [Photos](https://github.com/rootasjey/unsplasharp#photos)\n* [Collections](https://github.com/rootasjey/unsplasharp#collections)\n* [Users](https://github.com/rootasjey/unsplasharp#users)\n* [Search](https://github.com/rootasjey/unsplasharp#search)\n* [Stats](https://github.com/rootasjey/unsplasharp#stats)\n* [Custom Requests](https://github.com/rootasjey/unsplasharp#custom-requests)\n\n### Instanciate a new client\n\nIt's necessary to instanciate a new client with at least an application ID to start making requests.\n\n```csharp\nvar client = new Client(\"YOUR_APPLICATION_ID\");\n```\n\n### General\n\n#### Rates limits\nUnsplash has API requests [rates limits](https://unsplash.com/documentation#rate-limiting).\n\nAn Unsplashsharp client has two properties to help you monitor API calls:\n\nMax API calls allowed per hour\n\n* ```MaxRateLimit```\n\nAPI calls remaining for the current hour\n* ```RateLimitRemaining```\n\n```csharp\nif (client.RateLimitRemaining == 0) {\n  // Warning the user that he's to wait some time\n  // before using the app again.\n}\n```\n\n```csharp\nif (client.MaxRateLimit == 50) {\n  // Application is in dev mode.\n} else if (client.MaxRateLimit == 5000) {\n  // Application is in prod mode.\n} else { /* Unknown mode */ }\n```\n\n### Photos\n\n#### Get a single photo from an id\n\n```csharp\nvar photo = await client.GetPhoto(\"TPv9dh822VA\");\n\n// get a photo in the specified width and height in pixels\nvar photoWidthHeight = await client.GetPhoto(id: \"TPv9dh822VA\", width: 500, height: 500);\n```\n\n#### Get a random photo\n\n```csharp\nvar randomPhoto = await client.GetRandomPhoto();\n\n// using collections' IDs\nvar randomPhotoFromCollections = await client.GetRandomPhoto(new string[] { \"499830\", \"194162\" });\n\n// from a specific user\nvar randomPhotoFromUser = await client.GetRandomPhoto(count: 1, username: \"matthewkane\");\n\nvar randomPhotosFromQuery = await client.GetRandomPhoto(count: 3, query:\"woman\");\n```\n\n#### Get a list of all photos\n\n```csharp\nvar listPhotos = await client.ListPhotos();\n\nvar listPhotosPaged = await client.ListPhotos(page:2, perPage:15, orderBy: OrderBy.Popular);\n```\n\n### Collections\n\n#### Get a single collection from an id\n```csharp\nvar collection = await client.GetCollection(\"771520\");\n```\n\n#### Get a list of all collections\n```csharp\nvar listCollection = await client.ListCollections();\n```\n\n#### Get a list of featured collections\n```csharp\nvar listFeaturedCollection = await client.ListFeaturedCollections();\n```\n#### Get a collection's photos from a collection's id\n```csharp\nvar listPhotos = await client.GetCollectionPhotos(\"771520\");\n```\n\n#### Get related collections from a collection's id\n\n```csharp\nvar collectionsRelated = await client.ListRelatedCollections(\"771520\");\n```\n\n\n### Users\n\n#### Get a single user from his/her username\n\n```csharp\nvar user = await client.GetUser(\"unsplash\");\n\nvar userCustomProfileImage = client.GetUser(\"seteales\", width: 100, height: 100);\n```\n\n#### Get a list of user's collections\n\n```csharp\nvar userCollections = await client.ListUserCollections(\"unsplash\");\n```\n\n#### Get a list of user's photos\n\n```csharp\nvar userPhotos = await client.ListUserPhotos(\"seteales\");\n\nvar userPhotosCustomParam = await client.ListUserPhotos(\"seteales\", page: 2, perPage: 2, stats: true);\n```\n\n#### Get a list of user's liked photos\n\n```csharp\nvar userLikedPhotos = await client.ListUserLikedPhotos(\"seteales\");\n```\n\n#### Get an user's statistics\n\n```csharp\nvar userStats = await client.GetUserStats(\"seteales\");\n```\n\n\n### Search\n#### Search photos from a query\n\n```csharp\nvar photosFound = await client.SearchPhoto(\"mountains\");\n```\n\n#### Search collections from a query\n\n```csharp\nvar collectionsFound = await client.SearchCollections(\"mountains\");\n```\n\n#### Search users from a query\n\n```csharp\nvar usersFound = await client.SearchUsers(\"seteales\");\n```\n\n### Stats\n#### Get Unsplash [total stats](https://unsplash.com/documentation#totals)\n\n```csharp\nvar totalStats = await client.GetTotalStats();\n```\n\n#### Get Unsplash [monthly stats](https://unsplash.com/documentation#month)\n\n```csharp\nvar monthlyStats = await client.GetMonthlyStats();\n```\n\n### Custom Requests\n\nIn adition to the previous API methods, you can build and use custom URL's to fetch photos, photos' lists, and collections' lists.\n\nThere're also methods to search for collections, photos and users using a custom URL.\n\n#### Fetch a photo\n\n```csharp\nvar photo = await client.FetchPhoto(\"you_custom_url\");\n```\n\n#### Fetch a list of photos\n\n```csharp\nvar photos = await client.FetchPhotosList(\"you_custom_url\");\n```\n\n#### Fetch a list of collections\n\n```csharp\nvar collections = await client.FetchCollectionsList(\"you_custom_url\");\n```\n\n#### Search for photos using a specific search URL\n\n```csharp\nvar photosFound = await client.FetchSearchPhotosList(\"your_custom_url\");\n```\n\n#### Search for collections using a specific search URL\n\n```csharp\nvar collectionsFound = await client.FetchSearcCollectionsList(\"your_custom_url\");\n```\n\n#### Search for users using a specific search URL\n\n```csharp\nvar usersFound = await client.FetchSearcUsersList(\"your_custom_url\");\n```\n\n\n## Tests\n\nTests are under [UnsplashsharpTests](https://github.com/rootasjey/unsplasharp/tree/master/UnsplashsharpTest) project.\n\nThey check the Unsplash [API status](https://status.unsplash.com/) and that every methods in the lib works properly.\n\nIn this project, a dev API key is used which is limited to 50 requests per hour. So ensure you're not off limit.\n\n## Personal API key\n\nIf you want to get your personal API key from Unsplash:\n\n1. Go to [Unsplash](https://unsplash.com)\n2. Log in or create a new account\n3. In the top bar, click on _'API/Developers'_\n4. Go to _['Your applications'](https://unsplash.com/oauth/applications)_\n5. Click on _'New Application'_ to create a new one and get an API key (and a Secret).\n\n## Dependencies\n\n* **System.Text.Json** - High-performance JSON serialization (included in .NET Standard 2.0+)\n* **Microsoft.Extensions.Logging.Abstractions** - Structured logging support\n* **Microsoft.Extensions.Http** - IHttpClientFactory integration\n* **Polly** - Resilience and retry policies\n\n### Removed Dependencies\n* ~~Newtonsoft.Json~~ - Replaced with System.Text.Json for better performance\n\n## Resources\n\n* [Official Unsplash documentation](https://unsplash.com/documentation)\n\n## TODO\n\n* Add [user's authentication actions](https://unsplash.com/documentation#user-authentication)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frootasjey%2Funsplasharp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frootasjey%2Funsplasharp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frootasjey%2Funsplasharp/lists"}