diff --git a/backends/backends.go b/backends/backends.go
index 3df9f77..aad5af7 100644
--- a/backends/backends.go
+++ b/backends/backends.go
@@ -23,6 +23,7 @@ import (
 	"strings"
 
 	"github.com/spf13/viper"
+	"go.uploadedlobster.com/scotty/backends/deezer"
 	"go.uploadedlobster.com/scotty/backends/dump"
 	"go.uploadedlobster.com/scotty/backends/funkwhale"
 	"go.uploadedlobster.com/scotty/backends/jspf"
@@ -75,6 +76,7 @@ func GetBackends() []BackendInfo {
 }
 
 var knownBackends = map[string]func() models.Backend{
+	"deezer":        func() models.Backend { return &deezer.DeezerApiBackend{} },
 	"dump":          func() models.Backend { return &dump.DumpBackend{} },
 	"funkwhale":     func() models.Backend { return &funkwhale.FunkwhaleApiBackend{} },
 	"jspf":          func() models.Backend { return &jspf.JSPFBackend{} },
diff --git a/backends/deezer/auth.go b/backends/deezer/auth.go
new file mode 100644
index 0000000..53bfd98
--- /dev/null
+++ b/backends/deezer/auth.go
@@ -0,0 +1,83 @@
+/*
+Copyright © 2023 Philipp Wolfer <phw@uploadedlobster.com>
+
+Scotty is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later version.
+
+Scotty is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+Scotty. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+package deezer
+
+import (
+	"encoding/json"
+	"io"
+	"net/http"
+	"time"
+
+	"go.uploadedlobster.com/scotty/internal/auth"
+	"golang.org/x/oauth2"
+)
+
+type deezerStrategy struct {
+	conf oauth2.Config
+}
+
+func (s deezerStrategy) Config() oauth2.Config {
+	return s.conf
+}
+
+func (s deezerStrategy) AuthCodeURL(verifier string, state string) string {
+	return s.conf.AuthCodeURL(state, oauth2.AccessTypeOffline, oauth2.S256ChallengeOption(verifier))
+}
+
+func (s deezerStrategy) ExchangeToken(code auth.CodeResponse, verifier string) (*oauth2.Token, error) {
+	// Deezer has a non-standard token exchange, expecting all parameters in the URL's query
+	req, err := http.NewRequest(http.MethodGet, s.conf.Endpoint.TokenURL, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	q := req.URL.Query()
+	q.Add("app_id", s.conf.ClientID)
+	q.Add("secret", s.conf.ClientSecret)
+	q.Add("code", code.Code)
+	q.Add("output", "json")
+	req.URL.RawQuery = q.Encode()
+
+	res, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return nil, err
+	}
+
+	reqBody, err := io.ReadAll(res.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	token := deezerToken{}
+	if err = json.Unmarshal(reqBody, &token); err != nil {
+		return nil, err
+	}
+
+	return token.Token(), nil
+}
+
+type deezerToken struct {
+	AccessToken string `json:"access_token"`
+	ExpiresIn   int64  `json:"expires"`
+}
+
+func (t deezerToken) Token() *oauth2.Token {
+	token := &oauth2.Token{AccessToken: t.AccessToken}
+	if t.ExpiresIn > 0 {
+		token.Expiry = time.Now().Add(time.Duration(t.ExpiresIn * time.Second.Nanoseconds()))
+	}
+	return token
+}
diff --git a/backends/deezer/client.go b/backends/deezer/client.go
new file mode 100644
index 0000000..45119a5
--- /dev/null
+++ b/backends/deezer/client.go
@@ -0,0 +1,80 @@
+/*
+Copyright © 2023 Philipp Wolfer <phw@uploadedlobster.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+package deezer
+
+import (
+	"errors"
+	"strconv"
+
+	"github.com/go-resty/resty/v2"
+	"golang.org/x/oauth2"
+)
+
+const baseURL = "https://api.deezer.com/"
+const MaxItemsPerGet = 50
+const DefaultRateLimitWaitSeconds = 5
+
+type Client struct {
+	HttpClient *resty.Client
+	token      oauth2.TokenSource
+}
+
+func NewClient(token oauth2.TokenSource) Client {
+	client := resty.New()
+	client.SetBaseURL(baseURL)
+	client.SetHeader("Accept", "application/json")
+	client.SetRetryCount(5)
+	return Client{
+		HttpClient: client,
+		token:      token,
+	}
+}
+
+func (c Client) UserTracks(offset int, limit int) (result TracksResult, err error) {
+	const path = "/user/me/tracks"
+	request := c.HttpClient.R().
+		SetQueryParams(map[string]string{
+			"index": strconv.Itoa(offset),
+			"limit": strconv.Itoa(limit),
+		}).
+		SetResult(&result)
+	c.setToken(request)
+	response, err := request.Get(path)
+
+	if response.StatusCode() != 200 {
+		err = errors.New(response.String())
+	} else if result.Error != nil {
+		err = errors.New(result.Error.Message)
+	}
+	return
+}
+
+func (c Client) setToken(req *resty.Request) error {
+	tok, err := c.token.Token()
+	if err != nil {
+		return err
+	}
+
+	req.SetQueryParam("access_token", tok.AccessToken)
+	return nil
+}
diff --git a/backends/deezer/client_test.go b/backends/deezer/client_test.go
new file mode 100644
index 0000000..a1df199
--- /dev/null
+++ b/backends/deezer/client_test.go
@@ -0,0 +1,71 @@
+/*
+Copyright © 2023 Philipp Wolfer <phw@uploadedlobster.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+package deezer_test
+
+import (
+	"net/http"
+	"testing"
+
+	"github.com/jarcoal/httpmock"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"go.uploadedlobster.com/scotty/backends/deezer"
+	"golang.org/x/oauth2"
+)
+
+func TestNewClient(t *testing.T) {
+	token := oauth2.StaticTokenSource(&oauth2.Token{})
+	client := deezer.NewClient(token)
+	assert.IsType(t, deezer.Client{}, client)
+}
+
+func TestGetUserTracks(t *testing.T) {
+	defer httpmock.DeactivateAndReset()
+
+	token := oauth2.StaticTokenSource(&oauth2.Token{})
+	client := deezer.NewClient(token)
+	setupHttpMock(t, client.HttpClient.GetClient(),
+		"https://api.deezer.com/user/me/tracks",
+		"testdata/user-tracks.json")
+
+	result, err := client.UserTracks(0, 2)
+	require.NoError(t, err)
+
+	assert := assert.New(t)
+	assert.Equal(4, result.Total)
+	require.Len(t, result.Tracks, 2)
+	track1 := result.Tracks[0]
+	assert.Equal(int64(1700743848), track1.AddedAt)
+	assert.Equal("Never Take Me Alive", track1.Track.Title)
+	assert.Equal("Outland", track1.Track.Album.Title)
+}
+
+func setupHttpMock(t *testing.T, client *http.Client, url string, testDataPath string) {
+	httpmock.ActivateNonDefault(client)
+
+	responder, err := httpmock.NewJsonResponder(200, httpmock.File(testDataPath))
+	if err != nil {
+		t.Fatal(err)
+	}
+	httpmock.RegisterResponder("GET", url, responder)
+}
diff --git a/backends/deezer/deezer.go b/backends/deezer/deezer.go
new file mode 100644
index 0000000..d834080
--- /dev/null
+++ b/backends/deezer/deezer.go
@@ -0,0 +1,161 @@
+/*
+Copyright © 2023 Philipp Wolfer <phw@uploadedlobster.com>
+
+Scotty is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later version.
+
+Scotty is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+Scotty. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+package deezer
+
+import (
+	"fmt"
+	"math"
+	"net/url"
+	"sort"
+	"time"
+
+	"github.com/spf13/viper"
+	"go.uploadedlobster.com/scotty/internal/auth"
+	"go.uploadedlobster.com/scotty/models"
+	"golang.org/x/oauth2"
+)
+
+type DeezerApiBackend struct {
+	client       Client
+	clientId     string
+	clientSecret string
+}
+
+func (b *DeezerApiBackend) Name() string { return "deezer" }
+
+func (b *DeezerApiBackend) FromConfig(config *viper.Viper) models.Backend {
+	b.clientId = config.GetString("client-id")
+	b.clientSecret = config.GetString("client-secret")
+	return b
+}
+
+func (b *DeezerApiBackend) OAuth2Strategy(redirectUrl *url.URL) auth.OAuth2Strategy {
+	conf := oauth2.Config{
+		ClientID:     b.clientId,
+		ClientSecret: b.clientSecret,
+		Scopes: []string{
+			"offline_access,basic_access,listening_history",
+		},
+		RedirectURL: redirectUrl.String(),
+		Endpoint: oauth2.Endpoint{
+			AuthURL:  "https://connect.deezer.com/oauth/auth.php",
+			TokenURL: "https://connect.deezer.com/oauth/access_token.php",
+		},
+	}
+
+	return deezerStrategy{conf: conf}
+}
+
+func (b *DeezerApiBackend) OAuth2Setup(token oauth2.TokenSource) error {
+	b.client = NewClient(token)
+	return nil
+}
+
+func (b *DeezerApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult, progress chan models.Progress) {
+	// Choose a high offset, we attempt to search the loves backwards starting
+	// at the oldest one.
+	offset := math.MaxInt32
+	perPage := MaxItemsPerGet
+
+	defer close(results)
+	defer close(progress)
+
+	p := models.Progress{Total: int64(perPage)}
+	var totalCount int
+
+out:
+	for {
+		result, err := b.client.UserTracks(offset, perPage)
+		if err != nil {
+			progress <- p.Complete()
+			results <- models.LovesResult{Error: err}
+			return
+		}
+
+		// The offset was higher then the actual number of tracks. Adjust the offset
+		// and continue.
+		if offset >= result.Total {
+			p.Total = int64(result.Total)
+			totalCount = result.Total
+			offset = result.Total - perPage
+			if offset < 0 {
+				offset = 0
+			}
+			continue
+		}
+
+		count := len(result.Tracks)
+		if count == 0 {
+			break out
+		}
+
+		loves := make(models.LovesList, 0, perPage)
+		for _, track := range result.Tracks {
+			love := track.AsLove()
+			if love.Created.Unix() > oldestTimestamp.Unix() {
+				loves = append(loves, love)
+			} else {
+				totalCount -= 1
+				break
+			}
+		}
+
+		sort.Sort(loves)
+		results <- models.LovesResult{Loves: loves, Total: totalCount}
+		p.Elapsed += int64(count)
+		progress <- p
+
+		if offset <= 0 {
+			// This was the last request, no further results
+			break out
+		}
+
+		offset -= perPage
+		if offset < 0 {
+			offset = 0
+		}
+	}
+
+	progress <- p.Complete()
+}
+
+func (t LovedTrack) AsLove() models.Love {
+	love := models.Love{
+		Created: time.Unix(t.AddedAt, 0),
+		Track:   t.Track.AsTrack(),
+	}
+
+	return love
+}
+
+func (t Track) AsTrack() models.Track {
+	track := models.Track{
+		TrackName:      t.Title,
+		ReleaseName:    t.Album.Title,
+		ArtistNames:    []string{t.Artist.Name},
+		Duration:       time.Duration(t.Duration * int(time.Second)),
+		AdditionalInfo: map[string]any{},
+	}
+
+	info := track.AdditionalInfo
+	info["music_service"] = "deezer.com"
+	info["origin_url"] = t.Link
+	info["deezer_id"] = t.Link
+	info["deezer_album_id"] = fmt.Sprintf("https://www.deezer.com/track/%v", t.Album.Id)
+	info["deezer_artist_id"] = fmt.Sprintf("https://www.deezer.com/track/%v", t.Artist.Id)
+
+	return track
+}
diff --git a/backends/deezer/deezer_test.go b/backends/deezer/deezer_test.go
new file mode 100644
index 0000000..09e240b
--- /dev/null
+++ b/backends/deezer/deezer_test.go
@@ -0,0 +1,50 @@
+/*
+Copyright © 2023 Philipp Wolfer <phw@uploadedlobster.com>
+
+Scotty is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later version.
+
+Scotty is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+Scotty. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+package deezer_test
+
+import (
+	"encoding/json"
+	"os"
+	"testing"
+	"time"
+
+	"github.com/spf13/viper"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"go.uploadedlobster.com/scotty/backends/deezer"
+)
+
+func TestFromConfig(t *testing.T) {
+	config := viper.New()
+	config.Set("client-id", "someclientid")
+	config.Set("client-secret", "someclientsecret")
+	backend := (&deezer.DeezerApiBackend{}).FromConfig(config)
+	assert.IsType(t, &deezer.DeezerApiBackend{}, backend)
+}
+
+func TestLovedTrackAsLove(t *testing.T) {
+	data, err := os.ReadFile("testdata/track.json")
+	require.NoError(t, err)
+	track := deezer.LovedTrack{}
+	err = json.Unmarshal(data, &track)
+	require.NoError(t, err)
+	love := track.AsLove()
+	assert.Equal(t, time.Unix(1700743848, 0), love.Created)
+	assert.Equal(t, time.Duration(255*time.Second), love.Duration)
+	assert.Equal(t, "Never Take Me Alive", love.TrackName)
+	assert.Equal(t, "Outland", love.ReleaseName)
+	assert.Equal(t, "Spear Of Destiny", love.ArtistName())
+}
diff --git a/backends/deezer/models.go b/backends/deezer/models.go
new file mode 100644
index 0000000..7b54546
--- /dev/null
+++ b/backends/deezer/models.go
@@ -0,0 +1,66 @@
+/*
+Copyright © 2023 Philipp Wolfer <phw@uploadedlobster.com>
+
+Scotty is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later version.
+
+Scotty is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+Scotty. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+package deezer
+
+type Result struct {
+	Error *Error `json:"error,omitempty"`
+}
+
+type Error struct {
+	// {"error":{"type":"OAuthException","message":"Invalid OAuth access token.","code":300}}
+	Type    string `json:"type"`
+	Message string `json:"message"`
+	Code    int    `json:"code"`
+}
+
+type TracksResult struct {
+	Result
+	Next     string       `json:"next"`
+	Previous string       `json:"prev"`
+	Total    int          `json:"total"`
+	Tracks   []LovedTrack `json:"data"`
+}
+
+type Track struct {
+	Id       int    `json:"id"`
+	Type     string `json:"type"`
+	Title    string `json:"title"`
+	Link     string `json:"link"`
+	Duration int    `json:"duration"`
+	Rank     int    `json:"rank"`
+	Readable bool   `json:"readable"`
+	Explicit bool   `json:"explicit_lyrics"`
+	Album    Album  `json:"album"`
+	Artist   Artist `json:"artist"`
+}
+
+type LovedTrack struct {
+	Track
+	AddedAt int64 `json:"time_add"`
+}
+
+type Album struct {
+	Id        int    `json:"id"`
+	Type      string `json:"type"`
+	Title     string `json:"title"`
+	TrackList string `json:"tracklist"`
+}
+
+type Artist struct {
+	Id   int    `json:"id"`
+	Type string `json:"type"`
+	Name string `json:"name"`
+}
diff --git a/backends/deezer/models_test.go b/backends/deezer/models_test.go
new file mode 100644
index 0000000..0973338
--- /dev/null
+++ b/backends/deezer/models_test.go
@@ -0,0 +1,45 @@
+/*
+Copyright © 2023 Philipp Wolfer <phw@uploadedlobster.com>
+
+Scotty is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later version.
+
+Scotty is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+Scotty. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+package deezer_test
+
+import (
+	"encoding/json"
+	"os"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"go.uploadedlobster.com/scotty/backends/deezer"
+)
+
+func TestUserTracksResult(t *testing.T) {
+	data, err := os.ReadFile("testdata/user-tracks.json")
+	require.NoError(t, err)
+	result := deezer.TracksResult{}
+	err = json.Unmarshal(data, &result)
+	require.NoError(t, err)
+
+	assert := assert.New(t)
+	assert.Equal(4, result.Total)
+	assert.Equal("https://api.deezer.com/user/me/tracks?limit=2&index=2",
+		result.Next)
+	require.Len(t, result.Tracks, 2)
+	track1 := result.Tracks[0]
+	assert.Equal(int64(1700743848), track1.AddedAt)
+	assert.Equal("Never Take Me Alive", track1.Title)
+	assert.Equal("Outland", track1.Album.Title)
+	assert.Equal("Spear Of Destiny", track1.Artist.Name)
+}
diff --git a/backends/deezer/testdata/track.json b/backends/deezer/testdata/track.json
new file mode 100644
index 0000000..fd451c8
--- /dev/null
+++ b/backends/deezer/testdata/track.json
@@ -0,0 +1,37 @@
+{
+  "id": 3265090,
+  "readable": true,
+  "title": "Never Take Me Alive",
+  "link": "https:\/\/www.deezer.com\/track\/3265090",
+  "duration": 255,
+  "rank": 72294,
+  "explicit_lyrics": false,
+  "explicit_content_lyrics": 0,
+  "explicit_content_cover": 0,
+  "md5_image": "193e4db0eb58117978059acbffe79e93",
+  "time_add": 1700743848,
+  "album": {
+    "id": 311576,
+    "title": "Outland",
+    "cover": "https:\/\/api.deezer.com\/album\/311576\/image",
+    "cover_small": "https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/193e4db0eb58117978059acbffe79e93\/56x56-000000-80-0-0.jpg",
+    "cover_medium": "https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/193e4db0eb58117978059acbffe79e93\/250x250-000000-80-0-0.jpg",
+    "cover_big": "https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/193e4db0eb58117978059acbffe79e93\/500x500-000000-80-0-0.jpg",
+    "cover_xl": "https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/193e4db0eb58117978059acbffe79e93\/1000x1000-000000-80-0-0.jpg",
+    "md5_image": "193e4db0eb58117978059acbffe79e93",
+    "tracklist": "https:\/\/api.deezer.com\/album\/311576\/tracks",
+    "type": "album"
+  },
+  "artist": {
+    "id": 94057,
+    "name": "Spear Of Destiny",
+    "picture": "https:\/\/api.deezer.com\/artist\/94057\/image",
+    "picture_small": "https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/3fc9b790e88636a03d58f1fe5e74d37c\/56x56-000000-80-0-0.jpg",
+    "picture_medium": "https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/3fc9b790e88636a03d58f1fe5e74d37c\/250x250-000000-80-0-0.jpg",
+    "picture_big": "https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/3fc9b790e88636a03d58f1fe5e74d37c\/500x500-000000-80-0-0.jpg",
+    "picture_xl": "https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/3fc9b790e88636a03d58f1fe5e74d37c\/1000x1000-000000-80-0-0.jpg",
+    "tracklist": "https:\/\/api.deezer.com\/artist\/94057\/top?limit=50",
+    "type": "artist"
+  },
+  "type": "track"
+}
diff --git a/backends/deezer/testdata/user-tracks.json b/backends/deezer/testdata/user-tracks.json
new file mode 100644
index 0000000..59fe685
--- /dev/null
+++ b/backends/deezer/testdata/user-tracks.json
@@ -0,0 +1,80 @@
+{
+  "data": [
+    {
+      "id": 3265090,
+      "readable": true,
+      "title": "Never Take Me Alive",
+      "link": "https:\/\/www.deezer.com\/track\/3265090",
+      "duration": 255,
+      "rank": 72294,
+      "explicit_lyrics": false,
+      "explicit_content_lyrics": 0,
+      "explicit_content_cover": 0,
+      "md5_image": "193e4db0eb58117978059acbffe79e93",
+      "time_add": 1700743848,
+      "album": {
+        "id": 311576,
+        "title": "Outland",
+        "cover": "https:\/\/api.deezer.com\/album\/311576\/image",
+        "cover_small": "https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/193e4db0eb58117978059acbffe79e93\/56x56-000000-80-0-0.jpg",
+        "cover_medium": "https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/193e4db0eb58117978059acbffe79e93\/250x250-000000-80-0-0.jpg",
+        "cover_big": "https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/193e4db0eb58117978059acbffe79e93\/500x500-000000-80-0-0.jpg",
+        "cover_xl": "https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/193e4db0eb58117978059acbffe79e93\/1000x1000-000000-80-0-0.jpg",
+        "md5_image": "193e4db0eb58117978059acbffe79e93",
+        "tracklist": "https:\/\/api.deezer.com\/album\/311576\/tracks",
+        "type": "album"
+      },
+      "artist": {
+        "id": 94057,
+        "name": "Spear Of Destiny",
+        "picture": "https:\/\/api.deezer.com\/artist\/94057\/image",
+        "picture_small": "https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/3fc9b790e88636a03d58f1fe5e74d37c\/56x56-000000-80-0-0.jpg",
+        "picture_medium": "https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/3fc9b790e88636a03d58f1fe5e74d37c\/250x250-000000-80-0-0.jpg",
+        "picture_big": "https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/3fc9b790e88636a03d58f1fe5e74d37c\/500x500-000000-80-0-0.jpg",
+        "picture_xl": "https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/3fc9b790e88636a03d58f1fe5e74d37c\/1000x1000-000000-80-0-0.jpg",
+        "tracklist": "https:\/\/api.deezer.com\/artist\/94057\/top?limit=50",
+        "type": "artist"
+      },
+      "type": "track"
+    },
+    {
+      "id": 2510418,
+      "readable": true,
+      "title": "Voodoo Lady",
+      "link": "https:\/\/www.deezer.com\/track\/2510418",
+      "duration": 259,
+      "rank": 196860,
+      "explicit_lyrics": true,
+      "explicit_content_lyrics": 1,
+      "explicit_content_cover": 2,
+      "md5_image": "ca459f264d682177d1c8f7620100a8bc",
+      "time_add": 1700747083,
+      "album": {
+        "id": 246602,
+        "title": "The Distance To Here",
+        "cover": "https:\/\/api.deezer.com\/album\/246602\/image",
+        "cover_small": "https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/ca459f264d682177d1c8f7620100a8bc\/56x56-000000-80-0-0.jpg",
+        "cover_medium": "https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/ca459f264d682177d1c8f7620100a8bc\/250x250-000000-80-0-0.jpg",
+        "cover_big": "https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/ca459f264d682177d1c8f7620100a8bc\/500x500-000000-80-0-0.jpg",
+        "cover_xl": "https:\/\/e-cdns-images.dzcdn.net\/images\/cover\/ca459f264d682177d1c8f7620100a8bc\/1000x1000-000000-80-0-0.jpg",
+        "md5_image": "ca459f264d682177d1c8f7620100a8bc",
+        "tracklist": "https:\/\/api.deezer.com\/album\/246602\/tracks",
+        "type": "album"
+      },
+      "artist": {
+        "id": 168,
+        "name": "LIVE",
+        "picture": "https:\/\/api.deezer.com\/artist\/168\/image",
+        "picture_small": "https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/5d102eed174da4b807345125b3d955ef\/56x56-000000-80-0-0.jpg",
+        "picture_medium": "https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/5d102eed174da4b807345125b3d955ef\/250x250-000000-80-0-0.jpg",
+        "picture_big": "https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/5d102eed174da4b807345125b3d955ef\/500x500-000000-80-0-0.jpg",
+        "picture_xl": "https:\/\/e-cdns-images.dzcdn.net\/images\/artist\/5d102eed174da4b807345125b3d955ef\/1000x1000-000000-80-0-0.jpg",
+        "tracklist": "https:\/\/api.deezer.com\/artist\/168\/top?limit=50",
+        "type": "artist"
+      },
+      "type": "track"
+    }
+  ],
+  "total": 4,
+  "next": "https:\/\/api.deezer.com\/user\/me\/tracks?limit=2&index=2"
+}
diff --git a/scotty.example.toml b/scotty.example.toml
index e358bb5..0e09b70 100644
--- a/scotty.example.toml
+++ b/scotty.example.toml
@@ -76,6 +76,16 @@ backend       = "spotify"
 client-id     = ""
 client-secret = ""
 
+[service.deezer]
+# Read listens and loves from a Deezer account
+backend       = "deezer"
+# You need to register an application on https://developers.deezer.com/myapps
+# and set the client ID and client secret below.
+# When registering use "http://127.0.0.1:2222/callback/deezer" as the
+# callback URI.
+client-id     = ""
+client-secret = ""
+
 [service.dump]
 # This backend allows writing listens and loves as console output. Useful for
 # debugging the export from other services.