pySplash.py 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,942 @@
1
+ Metadata-Version: 2.4
2
+ Name: pySplash.py
3
+ Version: 1.0.0
4
+ Summary: A Python wrapper for the Unsplash API
5
+ Author: Sandeep Vattapparambil
6
+ License-Expression: MIT
7
+ Keywords: Unsplash,API,wrapper,Images,Photos
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Requires-Python: >=3.9
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: requests>=2.28.0
20
+ Requires-Dist: httpx>=0.24.0
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
23
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
24
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
25
+ Requires-Dist: ruff>=0.4.0; extra == "dev"
26
+ Dynamic: license-file
27
+
28
+ <p align="center">
29
+ <img src="assets/logo.png" alt="pySplash.py" width="600">
30
+ </p>
31
+
32
+ # pySplash.py v1.0.0
33
+
34
+ [![license](https://img.shields.io/github/license/Sandeepv68/pySplash.svg)](https://github.com/Sandeepv68/pySplash/blob/master/LICENSE) ![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg) ![GitHub issues](https://img.shields.io/github/issues/Sandeepv68/pySplash.svg) ![GitHub forks](https://img.shields.io/github/forks/Sandeepv68/pySplash.svg) ![GitHub stars](https://img.shields.io/github/stars/Sandeepv68/pySplash.svg)
35
+
36
+ pySplash.py is a simple, Python API wrapper for the popular [Unsplash](https://unsplash.com/) platform. The library is written in **Python**, supports both **sync** (via `requests`) and **async** (via `httpx`) usage, and can be used in any Python 3.9+ project.
37
+ Unsplash provides beautiful high quality free images and photos that you can download and use for any project without any attribution.
38
+
39
+ Before using the Unsplash API, you need to **register as a developer** and **read the API Guidelines.**
40
+
41
+ > **Note:** Every application must abide by the [API Guidelines](https://unsplash.com/documentation). Specifically, remember to hotlink images and trigger a download when appropriate.
42
+
43
+ ## Table of Contents
44
+ <!--ts-->
45
+ * [About](#pysplashpy-v100)
46
+ * [Installation](#installation)
47
+ * [Sample Usage](#sample-usage)
48
+ * [Development](#development)
49
+ * [Dependency](#dependency)
50
+ * [API Documentation](#api-documentation)
51
+ * [Schema](#schema)
52
+ * [Location](#location)
53
+ * [Summary Objects](#summary-objects)
54
+ * [Error Messages](#error-messages)
55
+ * [Authorization](#authorization)
56
+ * [Public Actions](#public-actions)
57
+ * [User Authentication](#user-authentication)
58
+ * [pySplash.py init()](#pysplashpy-init)
59
+ * [Generate Bearer Token](#generate-bearer-token)
60
+ * [Users APIs](#users-apis)
61
+ * [Get User's Public Profile](#get-users-public-profile)
62
+ * [Get User's Portfolio Link](#get-users-portfolio-link)
63
+ * [Get User's Photos](#get-users-photos)
64
+ * [Get User Liked Photos](#get-user-liked-photos)
65
+ * [Get User's Collections](#get-users-collections)
66
+ * [Get User's Statistics](#get-users-statistics)
67
+ * [Photos APIs](#photos-apis)
68
+ * [List Photos](#list-photos)
69
+ * [List Curated Photos](#list-curated-photos)
70
+ * [Get a Photo by Id](#get-a-photo-by-id)
71
+ * [Get a Random Photo](#get-a-random-photo)
72
+ * [Get a Photo's Statistics](#get-a-photos-statistics)
73
+ * [Get a Photo's Download Link](#get-a-photos-download-link)
74
+ * [Update a Photo](#update-a-photo)
75
+ * [Like a Photo](#like-a-photo)
76
+ * [Unlike a Photo](#unlike-a-photo)
77
+ * [Search APIs](#search-apis)
78
+ * [Search Photos](#search-photos)
79
+ * [Search Collections](#search-collections)
80
+ * [Search Users](#search-users)
81
+ * [Current User APIs](#current-user-apis)
82
+ * [Get the user's profile](#get-users-profile)
83
+ * [Update User's Profile](#update-users-profile)
84
+ * [Stats APIs](#stats-apis)
85
+ * [Totals](#stats-totals)
86
+ * [Months](#stats-month)
87
+ * [Collections APIs](#collections-apis)
88
+ * [Link Relations](#link-relations)
89
+ * [List Collections](#list-collections)
90
+ * [List Featured Collections](#list-featured-collections)
91
+ * [List Curated Collections](#list-curated-collections)
92
+ * [Get a Collection](#get-a-collection)
93
+ * [Get a Curated Collection](#get-a-curated-collection)
94
+ * [Get a Collection's Photos](#get-a-collections-photos)
95
+ * [Get a Curated Collection's Photos](#get-a-curated-collections-photos)
96
+ * [List a Collection's Related Collections](#list-a-collections-related-collections)
97
+ * [Create a New Collection](#create-a-new-collection)
98
+ * [Update an Existing Collection](#update-an-existing-collection)
99
+ * [Delete a Collection](#delete-a-collection)
100
+ * [Add a Photo to a Collection](#add-a-photo-to-a-collection)
101
+ * [Remove a Photo from a Collection](#remove-a-photo-from-a-collection)
102
+ * [Tests](#tests)
103
+ * [License](#license)
104
+ * [Acknowledgements](#acknowledgements)
105
+ <!--te-->
106
+
107
+ ## Installation
108
+
109
+ Install the package from PyPI
110
+ ```sh
111
+ pip install pysplash.py
112
+ ```
113
+
114
+ ### Sample usage
115
+ ```python
116
+ # Synchronous
117
+ from pySplash import PySplashApi
118
+
119
+ unsplash = PySplashApi()
120
+
121
+ unsplash.init(bearer_token="<bearer-token>")
122
+
123
+ result = unsplash.get_photo("<photo-id>")
124
+ print(result)
125
+ ```
126
+
127
+ ```python
128
+ # Asynchronous
129
+ import asyncio
130
+ from pySplash import PySplashApiAsync
131
+
132
+
133
+ async def main():
134
+ unsplash = PySplashApiAsync()
135
+ unsplash.init(bearer_token="<bearer-token>")
136
+ result = await unsplash.get_photo("<photo-id>")
137
+ print(result)
138
+
139
+
140
+ asyncio.run(main())
141
+ ```
142
+
143
+ ### Development
144
+ ```sh
145
+ pip install requests httpx pytest pytest-asyncio
146
+ pytest tests/ -v
147
+ ```
148
+
149
+ ### Dependency
150
+ This library depends on [requests](https://pypi.org/project/requests/) for synchronous HTTP, [httpx](https://pypi.org/project/httpx/) for asynchronous HTTP, and uses Python's built-in `hashlib` for SHA-256 hashing to generate required request headers for the [Unsplash API](https://unsplash.com/documentation).
151
+
152
+ ### API Documentation
153
+
154
+ ### Schema
155
+ #### Location
156
+ The API we are using is ```https://api.unsplash.com/```. Responses are sent as JSON.
157
+
158
+ #### Summary objects
159
+ When retrieving a list of objects, an abbreviated or summary version of that object is returned - i.e., a subset of its attributes. To get a full detailed version of that object, fetch it individually.
160
+
161
+ #### Error messages
162
+ If an error occurs, whether on the server or client side, the error message(s) will be returned in an ```errors``` array.
163
+ For example:
164
+ ```sh
165
+ 422 Unprocessable Entity
166
+ ```
167
+ ```python
168
+ {"errors": ["Username is missing", "Password cannot be blank"]}
169
+ ```
170
+
171
+ ### Authorization
172
+ #### Public Actions
173
+ Many actions can be performed without requiring authentication from a specific user. For example, downloading a photo does not require a user to log in.
174
+ To authenticate requests in this way, pass your application's access key via the HTTP ```Authorization``` header:
175
+ ```sh
176
+ Authorization: Client-ID YOUR_ACCESS_KEY
177
+ ```
178
+ You can also pass this value using a ```client_id``` query parameter:
179
+ ```sh
180
+ https://api.unsplash.com/photos/?client_id=YOUR_ACCESS_KEY
181
+ ```
182
+ If only your access key is sent, attempting to perform non-public actions that require user authorization will result in a ```401 Unauthorized response```.
183
+
184
+ #### User Authentication
185
+ The Unsplash API uses OAuth2 to authenticate and authorize Unsplash users. Unsplash's OAuth2 paths live at ```https://unsplash.com/oauth/```.
186
+
187
+ Before using pySplash.py:
188
+ - Developers are required to create a developer account from [Unsplash](https://unsplash.com/developers).
189
+ - Create a new App from Your Apps page.
190
+ - Get the ```Access Key```, ```Secret key```, ```Callback URLs```, and ```Authorization code```.
191
+ - If you have a Bearer Token, then its super, or else you can generate it using **pySplash.py**.
192
+ > **Note:** ```Authorization code``` can be obtained by clicking the ```Authorize``` link next to ```Callback URLs```. Also ```Authorization code``` is a one-time use code, you have to generate it again, if the action fails!.
193
+
194
+ #### pySplash.py init()
195
+ pySplash.py instance has to be initialized with your credentials obtained from Unsplash developer account for programatic access. These credentials information are passed in to the `init()` function as keyword arguments. The following example shows all the available options.
196
+
197
+ ```python
198
+ from pySplash import PySplashApi
199
+
200
+ UnsplashApi = PySplashApi()
201
+
202
+ UnsplashApi.init(
203
+ access_key="<api-key>",
204
+ secret_key="<secret-key>",
205
+ redirect_uri="<callback-url>",
206
+ code="<authorization-code>",
207
+ )
208
+ ```
209
+ If you have a `bearer_token`, then only bearer token has to be passed in.
210
+ ```python
211
+ UnsplashApi.init(bearer_token="<bearer-token>")
212
+ ```
213
+
214
+ #### Generate Bearer Token
215
+ A method to generate a Bearer Token for ```write_access``` to private data.
216
+ The ```init()``` method in this case requires `access_key`, `secret_key`, `redirect_uri`, and `code` to generate bearer token.
217
+ > **Note:** No Parameters are required for this function.
218
+
219
+ ```python
220
+ from pySplash import PySplashApi
221
+
222
+ UnsplashApi = PySplashApi()
223
+
224
+ UnsplashApi.init(
225
+ access_key="<api-key>",
226
+ secret_key="<secret-key>",
227
+ redirect_uri="<callback-url>",
228
+ code="<authorization-code>",
229
+ )
230
+
231
+ result = UnsplashApi.generate_bearer_token()
232
+ print(result)
233
+ ```
234
+ ```python
235
+ # Async version
236
+ result = await UnsplashApi.generate_bearer_token()
237
+ ```
238
+
239
+ If successful, the response body will be a JSON representation of your user's access token a.k.a bearer token:
240
+
241
+ ```sh
242
+ {
243
+ "access_token": "091343ce13c8ae780065ecb3b13dc903475dd22cb78a05503c2e0c69c5e98044",
244
+ "token_type": "bearer",
245
+ "scope": "public read_photos write_photos",
246
+ "created_at": 1436544465
247
+ }
248
+ ```
249
+ and once you have your ```bearer_token``` you can use it in your app like this:
250
+ ```python
251
+ UnsplashApi.init(bearer_token="<bearer-token>")
252
+ ```
253
+
254
+ ### Users APIs
255
+ #### Get User's Public Profile
256
+ Retrieves public details on a given user.
257
+ ```
258
+ GET /users/:username
259
+ ```
260
+ ##### Parameters
261
+
262
+ | Parameter | Type | Description | Optional | Default |
263
+ | ----- | ---- | ----------- | -------- | ------- |
264
+ | **username** | *str* | The username of the particular user | no |
265
+ | **width** | *int* | Width of the profile picture in pixels | yes |
266
+ | **height** | *int* | Height of the profile picture in pixels | yes |
267
+
268
+ > **Note:** When optional **height** & **width** are specified the profile image will be included in the "profile_image" object as "custom".
269
+
270
+ ```python
271
+ UnsplashApi.get_public_profile("<username>", 600, 600)
272
+ ```
273
+
274
+ #### Get User's Portfolio Link
275
+ Retrieves a single user's portfolio link.
276
+ ```
277
+ GET /users/:username/portfolio
278
+ ```
279
+ ##### Parameters
280
+
281
+ | Parameter | Type | Description | Optional | Default |
282
+ | ----- | ---- | ----------- | -------- | ------- |
283
+ | **username** | *str* | The username of the particular user | no |
284
+
285
+ ```python
286
+ UnsplashApi.get_user_portfolio("<username>")
287
+ ```
288
+
289
+ #### Get User's Photos
290
+ Gets a list of photos uploaded by a particular user.
291
+ ```
292
+ GET /users/:username/photos
293
+ ```
294
+ ##### Parameters
295
+
296
+ | Parameter | Type | Description | Optional | Default |
297
+ | ----- | ---- | ----------- | -------- | ------- |
298
+ | **username** | *str* | The username of the particular user | no |
299
+ | **page** | *int* | Page number to retrieve | yes | 1
300
+ | **per_page** | *int* | Number of items per page | yes | 10
301
+ | **stats** | *bool* | Show the stats for each user's photo | yes | False
302
+ | **resolution** | *str* | The frequency of the stats | yes | days
303
+ | **quantity** | *int* | The amount of for each stat | yes | 30
304
+ | **order_by** | *str* | How to sort the photos.(```Valid values: latest, oldest, popular```) | yes | latest
305
+
306
+ ```python
307
+ UnsplashApi.get_user_photos("<username>", 1, 10, False, "days", 30, "latest")
308
+ ```
309
+
310
+ #### Get User Liked Photos
311
+ Gets a list of photos liked by a user.
312
+ ```
313
+ GET /users/:username/likes
314
+ ```
315
+ ##### Parameters
316
+
317
+ | Parameter | Type | Description | Optional | Default |
318
+ | ----- | ---- | ----------- | -------- | ------- |
319
+ | **username** | *str* | The username of the particular user | no |
320
+ | **page** | *int* | Page number to retrieve | yes | 1
321
+ | **per_page** | *int* | Number of items per page | yes | 10
322
+ | **order_by** | *str* | How to sort the photos.(```Valid values: latest, oldest, popular```) | yes | latest
323
+
324
+ ```python
325
+ UnsplashApi.get_user_liked_photos("<username>", 1, 10, "latest")
326
+ ```
327
+
328
+ #### Get User's Collections
329
+ Gets a list of collections created by the user.
330
+ ```
331
+ GET /users/:username/collections
332
+ ```
333
+ ##### Parameters
334
+
335
+ | Parameter | Type | Description | Optional | Default |
336
+ | ----- | ---- | ----------- | -------- | ------- |
337
+ | **username** | *str* | The username of the particular user | no |
338
+ | **page** | *int* | Page number to retrieve | yes | 1
339
+ | **per_page** | *int* | Number of items per page | yes | 10
340
+
341
+ ```python
342
+ UnsplashApi.get_user_collections("<username>", 1, 10)
343
+ ```
344
+
345
+ #### Get User's Statistics
346
+ Gets a user's account statistics.
347
+ ```
348
+ GET /users/:username/statistics
349
+ ```
350
+
351
+ ##### Parameters
352
+
353
+ | Parameter | Type | Description | Optional | Default |
354
+ | ----- | ---- | ----------- | -------- | ------- |
355
+ | **username** | *str* | The username of the particular user | no |
356
+ | **resolution** | *str* | The frequency of the stats | yes | days
357
+ | **quantity** | *int* | The amount of for each stat | yes | 30
358
+
359
+ ```python
360
+ UnsplashApi.get_user_statistics("<username>", "days", 30)
361
+ ```
362
+
363
+ ### Photos APIs
364
+ #### List Photos
365
+ Gets a single page from the list of all photos.
366
+ ```
367
+ GET /photos
368
+ ```
369
+ ##### Parameters
370
+
371
+ | Parameter | Type | Description | Optional | Default |
372
+ | ----- | ---- | ----------- | -------- | ------- |
373
+ | **page** | *int* | Page number to retrieve | yes | 1
374
+ | **per_page** | *int* | Number of items per page | yes | 10
375
+ | **order_by** | *str* | How to sort the photos.(```Valid values: latest, oldest, popular```) | yes | latest
376
+
377
+ ```python
378
+ UnsplashApi.list_photos(1, 10, "latest")
379
+ ```
380
+
381
+ #### List Curated Photos
382
+ Gets a single page from the list of the curated photos.
383
+ ```
384
+ GET /photos/curated
385
+ ```
386
+ ##### Parameters
387
+
388
+ | Parameter | Type | Description | Optional | Default |
389
+ | ----- | ---- | ----------- | -------- | ------- |
390
+ | **page** | *int* | Page number to retrieve | yes | 1
391
+ | **per_page** | *int* | Number of items per page | yes | 10
392
+ | **order_by** | *str* | How to sort the photos.(```Valid values: latest, oldest, popular```) | yes | latest
393
+
394
+ ```python
395
+ UnsplashApi.list_curated_photos(1, 10, "latest")
396
+ ```
397
+
398
+ #### Get a Photo by Id
399
+ Retrieves a single photo.
400
+ ```
401
+ GET /photos/:id
402
+ ```
403
+ ##### Parameters
404
+
405
+ | Parameter | Type | Description | Optional | Default |
406
+ | ----- | ---- | ----------- | -------- | ------- |
407
+ | **id** | *str* | The photo's ID | no |
408
+ | **width** | *int* | Image width in pixels | yes |
409
+ | **height** | *int* | Image height in pixels | yes |
410
+ | **rect** | *str* |4 comma-separated integers representing x, y, width, height of the cropped rectangle | yes |
411
+ > **Note:** Supplying the optional **width** or **height** parameters will result
412
+ in the custom photo URL being added to the urls object:
413
+
414
+ ```python
415
+ UnsplashApi.get_photo("<id of the photo>", 500, 500, "0, 0, width, height")
416
+ ```
417
+
418
+ #### Get a Random Photo
419
+ Retrieves a single random photo, given optional filters.
420
+ ```
421
+ GET /photos/random
422
+ ```
423
+ ##### Parameters
424
+ > **Note:** All parameters are optional, and can be combined to narrow the pool of photos from which a random one will be chosen.
425
+
426
+ | Parameter | Type | Description | Optional | Default |
427
+ | ----- | ---- | ----------- | -------- | ------- |
428
+ | **collections** | *str* | The public collection ID('s) to filter selection. If multiple, comma-separated | yes |
429
+ | **featured** | *bool* | Limit selection to featured photos | yes | False
430
+ | **username** | *str* | Limit selection to a single user | yes |
431
+ | **query** | *str* | Limit selection to photos matching a search term | yes |
432
+ | **width** | *int* | The Image width in pixels | yes |
433
+ | **height** | *int* | The Image height in pixels | yes |
434
+ | **orientation** | *str* | Filter search results by photo orientation. (```Valid values are landscape, portrait, and squarish```) | yes | landscape
435
+ | **count** | *int* | The number of photos to return. (```max: 30```) | yes | 1
436
+
437
+ > **Note:** You can't use the collections and query parameters in the same request.
438
+ > When supplying a **count** parameter - and only then - the response will be an array of photos, even if the value of **count** is 1.
439
+
440
+ ```python
441
+ UnsplashApi.get_random_photo()
442
+ ```
443
+
444
+ #### Get a Photo's Statistics
445
+ Retrieves total number of downloads, views and likes of a single photo, as well as the historical breakdown of these stats in a specific timeframe (default is 30 days).
446
+ ```
447
+ GET /photos/:id/statistics
448
+ ```
449
+ ##### Parameters
450
+
451
+ | Parameter | Type | Description | Optional | Default |
452
+ | ----- | ---- | ----------- | -------- | ------- |
453
+ | **id** | *str* | The photo's ID | no |
454
+ | **resolution** | *str* | The frequency of the stats | yes | days
455
+ | **quantity** | *int* | The amount of for each stat | yes | 30
456
+ > **Note:** Currently, the only resolution param supported is "days". The quantity param can be any number between 1 and 30.
457
+
458
+ ```python
459
+ UnsplashApi.get_photo_statistics("<photo-id>", "days", 10)
460
+ ```
461
+
462
+ #### Get a Photo's Download Link
463
+ Retrieves a single photo's download link. Preferably hit this endpoint if a photo is downloaded in your application for use (example: to be displayed on a blog article, to be shared on social media, to be remixed, etc).
464
+ ```
465
+ GET /photos/:id/download
466
+ ```
467
+ ##### Parameters
468
+
469
+ | Parameter | Type | Description | Optional | Default |
470
+ | ----- | ---- | ----------- | -------- | ------- |
471
+ | **id** | *str* | The photo's ID | no |
472
+ > **Note:** This is different than the concept of a view, which is tracked automatically when you hotlink an image.
473
+
474
+ ```python
475
+ UnsplashApi.get_photo_link("<photo-id>")
476
+ ```
477
+
478
+ #### Update a Photo
479
+ Updates a photo on behalf of the logged-in user. This requires the ```write_photos``` scope and ```bearer_token```.
480
+ ```
481
+ PUT /photos/:id
482
+ ```
483
+ ##### Parameters
484
+
485
+ | Parameter | Type | Description | Optional | Default |
486
+ | ----- | ---- | ----------- | -------- | ------- |
487
+ | **id** | *str* | The photo's ID | no |
488
+ | **location** | *dict* | The location dict holding location data | yes |
489
+ | **exif** | *dict* | The exif dict holding exif data | yes |
490
+ > **Note:** **Exchangeable image file format** (officially Exif, according to JEIDA/JEITA/CIPA specifications) is a standard that specifies the formats for images, sound, and ancillary tags used by digital cameras (including smartphones), scanners and other systems handling image and sound files recorded by digital cameras. [Readmore](https://en.wikipedia.org/wiki/Exif)
491
+
492
+ ##### location & exif dict keys
493
+
494
+ | dict[key] | Description |
495
+ | ----- | ----------- |
496
+ | location[latitude] | The photo location's latitude (Optional) |
497
+ | location[longitude] | The photo location's longitude (Optional) |
498
+ | location[name] | The photo location's name (Optional) |
499
+ | location[city] | The photo location's city (Optional) |
500
+ | location[country] | The photo location's country (Optional) |
501
+ | location[confidential] | The photo location's confidentiality (Optional) |
502
+ | exif[make] | Camera's brand (Optional) |
503
+ | exif[model] | Camera's model (Optional) |
504
+ | exif[exposure_time] | Camera's exposure time (Optional) |
505
+ | exif[aperture_value] | Camera's aperture value (Optional) |
506
+ | exif[focal_length] | Camera's focal length (Optional) |
507
+ | exif[iso_speed_ratings] | Camera's iso (Optional) |
508
+
509
+ ```python
510
+ UnsplashApi.update_photo("<photo-id>", {"country": "INDIA"}, {"make": "Redmi Note 3"})
511
+ ```
512
+
513
+ #### Like a Photo
514
+ Likes a photo on behalf of the logged-in user. This requires the ```write_likes``` scope.
515
+ ```
516
+ POST /photos/:id/like
517
+ ```
518
+ ##### Parameters
519
+
520
+ | Parameter | Type | Description | Optional | Default |
521
+ | ----- | ---- | ----------- | -------- | ------- |
522
+ | **id** | *str* | The photo's ID | no |
523
+ > **Note:** This action is idempotent; sending the POST request to a single photo multiple times has no additional effect.
524
+
525
+ ```python
526
+ UnsplashApi.like_photo("<photo-id>")
527
+ ```
528
+
529
+ #### Unlike a Photo
530
+ Removes a user's like of a photo.
531
+ ```
532
+ DELETE /photos/:id/like
533
+ ```
534
+ ##### Parameters
535
+
536
+ | Parameter | Type | Description | Optional | Default |
537
+ | ----- | ---- | ----------- | -------- | ------- |
538
+ | **id** | *str* | The photo's ID | no |
539
+ > **Note:** This action is idempotent; sending the DELETE request to a single photo multiple times has no additional effect.
540
+
541
+ ```python
542
+ UnsplashApi.unlike_photo("<photo-id>")
543
+ ```
544
+
545
+ ### Search APIs
546
+ #### Search Photos
547
+ Gets a single page of photo results for a particular query.
548
+ ```
549
+ GET /search/photos
550
+ ```
551
+ ##### Parameters
552
+
553
+ | Parameter | Type | Description | Optional | Default |
554
+ | ----- | ---- | ----------- | -------- | ------- |
555
+ | **query** | *str* | The search query | no |
556
+ | **page** | *int* | Page number to retrieve | yes | 1
557
+ | **per_page** | *int* | Number of items per page | yes | 10
558
+ | **collections** | *str* | Collection ID('s) to narrow search. If multiple, comma-separated. | yes |
559
+ | **orientation** | *str* | Filter search results by photo orientation. (```Valid values are landscape, portrait, and squarish.```) | yes | landscape
560
+
561
+ ```python
562
+ UnsplashApi.search("cars", 1, 10, "", "landscape")
563
+ ```
564
+
565
+ #### Search Collections
566
+ Gets a single page of collection results for a query.
567
+ ```
568
+ GET /search/collections
569
+ ```
570
+ ##### Parameters
571
+
572
+ | Parameter | Type | Description | Optional | Default |
573
+ | ----- | ---- | ----------- | -------- | ------- |
574
+ | **query** | *str* | The search query | no |
575
+ | **page** | *int* | Page number to retrieve | yes | 1
576
+ | **per_page** | *int* | Number of items per page | yes | 10
577
+
578
+ ```python
579
+ UnsplashApi.search_collections("cars", 1, 10)
580
+ ```
581
+
582
+ #### Search Users
583
+ Gets a single page of user results for a query.
584
+ ```
585
+ GET /search/users
586
+ ```
587
+ ##### Parameters
588
+
589
+ | Parameter | Type | Description | Optional | Default |
590
+ | ----- | ---- | ----------- | -------- | ------- |
591
+ | **query** | *str* | The search query | no |
592
+ | **page** | *int* | Page number to retrieve | yes | 1
593
+ | **per_page** | *int* | Number of items per page | yes | 10
594
+
595
+ ```python
596
+ UnsplashApi.search_users("<search-keyword>", 1, 10)
597
+ ```
598
+
599
+ ### Current User APIs
600
+ #### Get User's Profile
601
+ Gets the current User's profile. To access a user's private data, the user is required to authorize the ```read_user``` scope. Without it, this request will return a ```403 Forbidden response```.
602
+ ```
603
+ GET /me
604
+ ```
605
+ > **Note:** No Parameters are required.
606
+
607
+ > **Note:** Without a Bearer token (i.e. using a ```Client-ID token```) this request will return a ```401 Unauthorized``` response.
608
+
609
+ ```python
610
+ UnsplashApi.get_current_user_profile()
611
+ ```
612
+
613
+ #### Update User's Profile
614
+ Updates the current User's profile.
615
+ ```
616
+ PUT /me
617
+ ```
618
+ ##### Parameters
619
+
620
+ | Parameter | Type | Description | Optional | Default |
621
+ | ----- | ---- | ----------- | -------- | ------- |
622
+ | username | *str* | The username of the current user | yes |
623
+ | first_name | *str* | The first name of the current user | yes |
624
+ | last_name | *str* | The last name of the current user | yes |
625
+ | email | *str* | The email id of the current user | yes |
626
+ | url | *str* | The Portfolio/personal URL of the current user | yes |
627
+ | location | *str* | The location of the current user | yes |
628
+ | bio | *str* | The About/bio of the current user | yes |
629
+ | instagram_username | *str* | The Instagram username of the current user | yes |
630
+ > **Note:** This action requires the ```write_user scope```. Without it, it will return a ```403 Forbidden response```.
631
+
632
+ ```python
633
+ UnsplashApi.update_current_user_profile(
634
+ "<username>", "<first_name>", "<last_name>", "<email>", "<url>", "<location>", "<bio>", "<instagram_username>"
635
+ )
636
+ ```
637
+
638
+ ### Stats APIs
639
+ #### Stats Totals
640
+ Gets a list of counts for all of Unsplash.
641
+ ```
642
+ GET /stats/total
643
+ ```
644
+ ```python
645
+ UnsplashApi.get_stats_totals()
646
+ ```
647
+ #### Response
648
+ ```sh
649
+ 200 OK
650
+ ```
651
+ ```sh
652
+ {
653
+ "total_stats": {
654
+ "photos": 10000,
655
+ "downloads": 2000,
656
+ "views": 5000,
657
+ "likes": 800,
658
+ "photographers": 100,
659
+ "pixels": 200000,
660
+ "downloads_per_second": 10,
661
+ "views_per_second": 20,
662
+ "developers": 20,
663
+ "applications": 50,
664
+ "requests": 8000
665
+ }
666
+ }
667
+ ```
668
+ #### Stats Month
669
+ Gets the overall Unsplash stats for the past 30 days.
670
+ ```
671
+ GET /stats/month
672
+ ```
673
+ ```python
674
+ UnsplashApi.get_stats_month()
675
+ ```
676
+ #### Response
677
+ ```sh
678
+ 200 OK
679
+ ```
680
+ ```sh
681
+ {
682
+ "month_stats": {
683
+ "downloads": 20,
684
+ "views": 200,
685
+ "likes": 60,
686
+ "new_photos": 10,
687
+ "new_photographers": 5,
688
+ "new_pixels": 2000,
689
+ "new_developers": 8,
690
+ "new_applications": 5,
691
+ "new_requests": 100
692
+ }
693
+ }
694
+ ```
695
+
696
+ ### Collections APIs
697
+ #### Link Relations
698
+ Collections have the following link relations:
699
+
700
+ | rel | Description |
701
+ | --- | ----------- |
702
+ | ```self``` | API location of this collection |
703
+ | ```html``` | HTML location of this collection |
704
+ | ```photos``` | API location of this collection's photos |
705
+ | ```related``` | API location of this collection's related collections (Non-curated collections only) |
706
+ | ```download``` | Download location of this collection's zip file (Curated collections only) |
707
+
708
+ #### List Collections
709
+ Gets a single page from the list of all collections.
710
+ ```
711
+ GET /collections
712
+ ```
713
+ ##### Parameters
714
+
715
+ | Parameter | Type | Description | Optional | Default |
716
+ | ----- | ---- | ----------- | -------- | ------- |
717
+ | **page** | *int* | Page number to retrieve | yes | 1
718
+ | **per_page** | *int* | Number of items per page | yes | 10
719
+ ```python
720
+ UnsplashApi.list_collections()
721
+ ```
722
+
723
+ #### List Featured Collections
724
+ Gets a single page from the list of featured collections.
725
+ ```
726
+ GET /collections/featured
727
+ ```
728
+ ##### Parameters
729
+
730
+ | Parameter | Type | Description | Optional | Default |
731
+ | ----- | ---- | ----------- | -------- | ------- |
732
+ | **page** | *int* | Page number to retrieve | yes | 1
733
+ | **per_page** | *int* | Number of items per page | yes | 10
734
+ ```python
735
+ UnsplashApi.list_featured_collections()
736
+ ```
737
+
738
+ #### List Curated Collections
739
+ Gets a single page from the list of curated collections.
740
+ ```
741
+ GET /collections/curated
742
+ ```
743
+ ##### Parameters
744
+
745
+ | Parameter | Type | Description | Optional | Default |
746
+ | ----- | ---- | ----------- | -------- | ------- |
747
+ | **page** | *int* | Page number to retrieve | yes | 1
748
+ | **per_page** | *int* | Number of items per page | yes | 10
749
+ ```python
750
+ UnsplashApi.list_curated_collections()
751
+ ```
752
+
753
+ #### Get a Collection
754
+ Retrieves a single collection. To view a user's private collections, the ```read_collections``` scope is required.
755
+ ```
756
+ GET /collections/:id
757
+ ```
758
+ ##### Parameters
759
+
760
+ | Parameter | Type | Description | Optional | Default |
761
+ | ----- | ---- | ----------- | -------- | ------- |
762
+ | **id** | *str* | The Collection ID | no |
763
+ ```python
764
+ UnsplashApi.get_collection("<collection-id>")
765
+ ```
766
+
767
+ #### Get a Curated Collection
768
+ Retrieves a single curated collection. To view a user's private collections, the ```read_collections``` scope is required.
769
+ ```
770
+ GET /collections/curated/:id
771
+ ```
772
+ ##### Parameters
773
+
774
+ | Parameter | Type | Description | Optional | Default |
775
+ | ----- | ---- | ----------- | -------- | ------- |
776
+ | **id** | *str* | The Collection ID | no |
777
+ ```python
778
+ UnsplashApi.get_curated_collection("<curated-collection-id>")
779
+ ```
780
+
781
+ #### Get a Collection's Photos
782
+ Retrieves a collection's photos.
783
+ ```
784
+ GET /collections/:id/photos
785
+ ```
786
+ ##### Parameters
787
+
788
+ | Parameter | Type | Description | Optional | Default |
789
+ | ----- | ---- | ----------- | -------- | ------- |
790
+ | **id** | *str* | The Collection ID | no |
791
+ | **page** | *int* | Page number to retrieve | yes | 1
792
+ | **per_page** | *int* | Number of items per page | yes | 10
793
+ ```python
794
+ UnsplashApi.get_collection_photos("<collection-id>", 1, 10)
795
+ ```
796
+
797
+ #### Get a Curated Collection's Photos
798
+ Retrieves a curated collection's photos.
799
+ ```
800
+ GET /collections/curated/:id/photos
801
+ ```
802
+ ##### Parameters
803
+
804
+ | Parameter | Type | Description | Optional | Default |
805
+ | ----- | ---- | ----------- | -------- | ------- |
806
+ | **id** | *str* | The Collection ID | no |
807
+ | **page** | *int* | Page number to retrieve | yes | 1
808
+ | **per_page** | *int* | Number of items per page | yes | 10
809
+ ```python
810
+ UnsplashApi.get_curated_collection_photos("<curated-collection-id>", 1, 10)
811
+ ```
812
+
813
+ #### List a Collection's Related Collections
814
+ Retrieves a list of collections related to this one.
815
+ ```
816
+ GET /collections/:id/related
817
+ ```
818
+ ##### Parameters
819
+
820
+ | Parameter | Type | Description | Optional | Default |
821
+ | ----- | ---- | ----------- | -------- | ------- |
822
+ | **id** | *str* | The Collection ID | no |
823
+ ```python
824
+ UnsplashApi.list_related_collections("<collection-id>")
825
+ ```
826
+
827
+ #### Create a New Collection
828
+ Creates a new collection. This requires the ```write_collections``` scope.
829
+ ```
830
+ POST /collections
831
+ ```
832
+ ##### Parameters
833
+
834
+ | Parameter | Type | Description | Optional | Default |
835
+ | ----- | ---- | ----------- | -------- | ------- |
836
+ | **title** | *str* | The title of the collection | no |
837
+ | **description** | *str* | The collection's description | yes |
838
+ | **private** | *bool* | Whether to make this collection private | yes | False
839
+ ```python
840
+ UnsplashApi.create_collection("<collection-name>", "<description>", False)
841
+ ```
842
+
843
+ #### Update an Existing Collection
844
+ Updates an existing collection belonging to the logged-in user. This requires the ```write_collections``` scope.
845
+ ```
846
+ PUT /collections/:id
847
+ ```
848
+ ##### Parameters
849
+
850
+ | Parameter | Type | Description | Optional | Default |
851
+ | ----- | ---- | ----------- | -------- | ------- |
852
+ | **id** | *str* | The collection id | no |
853
+ | **title** | *str* | The title of the collection | yes |
854
+ | **description** | *str* | The collection's description | yes |
855
+ | **private** | *bool* | Whether to make this collection private | yes | False
856
+ ```python
857
+ UnsplashApi.update_collection("<collection-id>", "<collection-name>", "<description>", False)
858
+ ```
859
+
860
+ #### Delete a Collection
861
+ Deletes a collection belonging to the logged-in user. This requires the ```write_collections``` scope.
862
+ ```
863
+ DELETE /collections/:id
864
+ ```
865
+ ##### Parameters
866
+
867
+ | Parameter | Type | Description | Optional | Default |
868
+ | ----- | ---- | ----------- | -------- | ------- |
869
+ | **id** | *str* | The Collection ID | no |
870
+ ```python
871
+ UnsplashApi.delete_collection("<collection-id>")
872
+ ```
873
+
874
+ #### Add a Photo to a Collection
875
+ Adds a photo to one of the logged-in user's collections. Requires the ```write_collections``` scope.
876
+ ```
877
+ POST /collections/:collection_id/add
878
+ ```
879
+ ##### Parameters
880
+
881
+ | Parameter | Type | Description | Optional | Default |
882
+ | ----- | ---- | ----------- | -------- | ------- |
883
+ | **collection_id** | *str* | The Collection ID | no |
884
+ | **photo_id** | *str* | The Photo ID | no |
885
+ > **Note:** If the photo is already in the collection, this action has no effect.
886
+
887
+ ```python
888
+ UnsplashApi.add_photo_to_collection("<collection-id>", "<photo-id>")
889
+ ```
890
+
891
+ #### Remove a Photo from a Collection
892
+ Removes a photo from one of the logged-in user's collections. Requires the ```write_collections``` scope.
893
+ ```
894
+ DELETE /collections/:collection_id/remove
895
+ ```
896
+ ##### Parameters
897
+
898
+ | Parameter | Type | Description | Optional | Default |
899
+ | ----- | ---- | ----------- | -------- | ------- |
900
+ | **collection_id** | *str* | The Collection ID | no |
901
+ | **photo_id** | *str* | The Photo ID | no |
902
+ ```python
903
+ UnsplashApi.remove_photo_from_collection("<collection-id>", "<photo-id>")
904
+ ```
905
+
906
+ ### Tests
907
+ pySplash.py uses `pytest` and `pytest-asyncio` as the testing environment. Test files are available in the `tests/` folder.
908
+
909
+ ```sh
910
+ pip install pytest pytest-asyncio
911
+ pytest tests/ -v
912
+ ```
913
+
914
+ ### License
915
+ The MIT License
916
+
917
+ Copyright (c) 2018- Sandeep Vattapparambil, http://www.sandeepv.in
918
+
919
+ Permission is hereby granted, free of charge, to any person obtaining a copy
920
+ of this software and associated documentation files (the "Software"), to deal
921
+ in the Software without restriction, including without limitation the rights
922
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
923
+ copies of the Software, and to permit persons to whom the Software is
924
+ furnished to do so, subject to the following conditions:
925
+
926
+ The above copyright notice and this permission notice shall be included in
927
+ all copies or substantial portions of the Software.
928
+
929
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
930
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
931
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
932
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
933
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
934
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
935
+ THE SOFTWARE.
936
+
937
+
938
+ ### Acknowledgements
939
+ Thanks, and Kudos to team [Unsplash](https://unsplash.com/) for creating a wonderful platform for sharing
940
+ beautiful high quality free images and photos.
941
+
942
+ Made with :heart: by [Sandeep Vattapparambil](https://github.com/Sandeepv68).