{"id":30754303,"url":"https://github.com/reddit/node-api-client","last_synced_at":"2025-09-04T09:09:20.175Z","repository":{"id":23772673,"uuid":"27147713","full_name":"reddit/node-api-client","owner":"reddit","description":null,"archived":false,"fork":false,"pushed_at":"2023-03-18T17:02:03.000Z","size":1048,"stargazers_count":91,"open_issues_count":4,"forks_count":30,"subscribers_count":84,"default_branch":"master","last_synced_at":"2025-06-25T04:18:42.442Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/reddit.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-11-25T21:51:35.000Z","updated_at":"2025-05-10T04:23:39.000Z","dependencies_parsed_at":"2024-06-19T00:46:35.400Z","dependency_job_id":"b1b72860-5256-447b-a44f-12c0d39e082e","html_url":"https://github.com/reddit/node-api-client","commit_stats":{"total_commits":625,"total_committers":22,"mean_commits":28.40909090909091,"dds":0.6255999999999999,"last_synced_commit":"49f31a352b5d9df32e772f5415c104013734a0c5"},"previous_names":[],"tags_count":241,"template":false,"template_full_name":null,"purl":"pkg:github/reddit/node-api-client","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reddit%2Fnode-api-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reddit%2Fnode-api-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reddit%2Fnode-api-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reddit%2Fnode-api-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reddit","download_url":"https://codeload.github.com/reddit/node-api-client/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reddit%2Fnode-api-client/sbom","scorecard":{"id":767552,"data":{"date":"2025-08-11","repo":{"name":"github.com/reddit/node-api-client","commit":"49f31a352b5d9df32e772f5415c104013734a0c5"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.3,"checks":[{"name":"Code-Review","score":4,"reason":"Found 8/20 approved changesets -- score normalized to 4","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":9,"reason":"security policy file detected","details":["Info: security policy file detected: github.com/reddit/.github/SECURITY.md:1","Info: Found linked content: github.com/reddit/.github/SECURITY.md:1","Warn: One or no descriptive hints of disclosure, vulnerability, and/or timelines in security policy","Info: Found text in security policy: github.com/reddit/.github/SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 18 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-23T01:16:12.989Z","repository_id":23772673,"created_at":"2025-08-23T01:16:12.989Z","updated_at":"2025-08-23T01:16:12.989Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273581276,"owners_count":25131393,"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","status":"online","status_checked_at":"2025-09-04T02:00:08.968Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2025-09-04T09:08:04.604Z","updated_at":"2025-09-04T09:09:20.156Z","avatar_url":"https://github.com/reddit.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"@r/api-client\n======\n\n[![Build Status](https://travis-ci.org/reddit/node-api-client.svg?branch=3X)](https://travis-ci.org/reddit/node-api-client)\n\nA reddit API library for node and browsers.\n\n```javascript\n// Require snoode.\nimport APIOptions from '@r/api-client';\nimport { collections } from '@r/api-client';\nconst { PostsFromSubreddit } = collections;\n\nimport { each } from 'lodash/collection';\n\nlet frontpage = await PostsFromSubreddit.fetch(APIOptions, 'highqualitygifs')\neach(Array(10), async () =\u003e {\n  frontpage = await frontpage.withNextPage(APIOptions);\n});\n\nfrontpage.posts; // ~275 glorious gifs\n\n// Example with auth.\n// Pass in an oauth token and new origin to `withConfig`, which returns\n// a new instance that inherits the config from the existing api instance\n// merged with the new config.\nimport { optionsWithAuth, collections } from '@r/api-client';\nconst { SavedPostsAndComments } = collections;\n\nconst myOauthToken = 'abcdef1234567890';\nconst authedOptions = optionsWithAuth(myOauthToken);\n\nconst dankestMemes = await SavedPostsAndComments.fetch(authedOptions, 'my-user-name');\nconsole.log(dankestMems.postsAndComments);\n```\n\n### API endpoints\nAt its core, the api is made up of ApiEndpoints, APIResponses, Models, and Records. ApiEndpoints all use functions from  [APIRequestUtils](/apiBase/APIRequestUtils.es6.js). It provides an easy way to build out a idealized restful wrapper for the api. Currently instances of the api endpoints are bound to API class (exported default when you import from the api client module). Using it looks like\n\n```javascript\n// Require snoode.\nimport APIOptions from '@r/api-client';\nimport { endpoints } from '@r/api-client';\nconst { PostsEndpoint, CommentsEndpoint } = endpoints;\n\n// Example call to get links for /r/homebrewing.\nPostsEndpoint.get(APIOptions, {\n  subreddit: 'homebrewing',\n}).then(res =\u003e {\n  console.log(res.results);\n  console.log(res.getModelFromRecord(res.results[0]));\n});\n\n// Example call to get all comments for this particular listing.\nCommentsEndpoint.get(APIOptions, {\n  linkId: 't3_ib4bk'\n}).then(res =\u003e {\n  console.log(res.results);\n  console.log(res.getModelFromRecord(res.results[0]));\n  console.log(res.comments);\n  console.log(res.links);\n});\n```\n\n##### NEW from 3.5.X+: The apiclient endpoint and collection signatures have now changed to take an APIOptions object instead of an API instance. Please note that while the imports are little verbose now, there will be a subsequent minor version change which will allow piecemeal importing of only the code you want from the api. This will make imports cleaner and your payload smaller.\n\n### Models and Records\nA [Record](/apiBase/Record.es6.js) is essentially a tuple of `(\u003cModelType\u003e, \u003cModelId)`.\n```javascript\nimport { models } from '@r/api-client';\nconst { Record } = models;\nconst LinkRecord = new Record(LINK_TYPE, 't3_4gonrl');\n```\n\n##### A note on pagination (3.12.0+):\nRecords have a property called paginationId. By default, this will be the same as uuid. This is useful for when the uuid of your model is different than the id you'll be using for pagination. See [Subreddit's Model](/models2/Subreddit.es6.js) for an example of a Model with a different UUID than paginationId.\n\nThey are produced by [Models](/apiBase/Model.es6.js).\nThe Model class provides type checking and immutability for your api data.\nYou define models in terms of Properties and types.\nExposed models for the Reddit API all inherit from [RedditModel](/apiBase/RedditMobile.es6.js).\n\n```javascript\nimport { models } from '@r/api-client';\nconst { RedditModel } = models;\n\nconst T = RedditModel.TYPES;\nclass Post extends RedditModel {\n  static type = 'post';\n\n  static PROPERTIES = {\n    id: T.string,\n    author: T.string,\n    clean_url: T.link,\n    score: T.number,\n    sr_detail: T.nop, // just use the api object, nested parsing coming soon(tm)\n  };\n}\n```\n\nYou can also alias names from the api that aren't javascript'y to a different name. You'll still have to provide a type alias.\n```javascript\nimport { models } from '@r/api-client';\nconst { Model } = models;\n\nconst T = Model.TYPES;\nclass Post extends Model {\n  static type = 'link';\n\n  static PROPERTIES = {\n    linkFlairCSSClass: T.string,\n    linkFlairText: T.string,\n  };\n\n  static API_ALIASES = {\n    link_flair_css_class: 'linkFlairCSSClass'\n    link_flair_text: 'linkFlairText',\n  };\n}\n```\n\nTypes are defined in terms of functions, that given any input, should return an output that is considered valid for that 'type'. e.g. T.array is a function than when given `undefined` or `2`, returns an empty array. But when passes a normal array, it passes it back. You can use this to do encode things like `T.link`, which take an input, validate that its a string, and then if its a reddit link, turn it into a relative url.\n\n```javascript\nimport { models } from '@r/api-client';\nconst { Model } = models;\n\nconst T = Model.TYPES;\nclass Post extends Model {\n  static PROPERTIES = {\n    cleanURL: T.link,\n  };\n\n  static API_ALIASES = {\n    clean_url: 'cleanURL',\n  };\n}\n```\n\nYou can also make properties that are derived from the raw json object from the api\nThis is useful for when you want to make a property that relies on multiple\nfields from the raw json, or when you want to make a new version of the field\nthat differs from the api (be careful). Here's an example for reddit modbile\n```javascript\nimport { models } from '@r/api-client';\nconst { Model } = models;\n\nconst T = MODEL.TYPES;\nclass Post extends Model {\n  static PROPERTIES = {\n    preview: T.nop,\n  };\n\n  static DERIVED_PROPERTIES = {\n    preview: (data) =\u003e {\n      if (!data.preview) {\n        // build a preview image based on media_oembed or the thumbanil\n        return ...\n      }\n\n      return data.preview;\n    },\n  };\n}\n```\n\n### APIResponses\n[APIResponse.es6.js](/apiBase/APIResponse.es6.js) defines the primary classes used to interact with responses from the api. (NOTE: we're still transitioning all the endpoints, but lots of them work). APIResponse embodies our opinionated view on managing your API data. Responses are normalized, and accessed in terms of records.\n\n```javascript\nimport APIOptions from '@r/api-client';\nimport { endpoints } from '@r/api-client'\nconst { PostsEndpoint } = endpoints;\n\nconst postsResponse = await PostsEndpoint.get(APIOptions, { subredditName: 'reactjs'});\npostsResponse.results; // an array of \u003cRecord\u003e\npostsResponse.links; // a dictionary of \u003cLinkId\u003e : \u003cLinkModel\u003e\n```\n\nFor cases where you want pagination, there are helpers provided by\n#### [APIResponsePaging.es6.js](/apiBase/APIResponsePaging.es6.js);\n```javascript\nimport { APIResponsePaging } from '@r/api-client';\nconst { afterResponse } = APIResponsePaging;\n\n// get the id of the last link in an api reponse, only if there's more data\n// to fetch\nafterResponse(PostsEndpoint.get(APIOptions, { subredditName: 'reactjs' }))\n```\n\n#### [MergedResponses](/apiBase/APIResponse.es6.js) handle casses where you have paginated data. Once you've fetched the next page, you can merge it with the first page to have one response represent your entire list of data.\n\n```javascript\nimport APIOptions from '@r/api-client';\nimport { endpoints } from '@r/api-client'\nconst { PostsEndpoint } = endpoints;\n\nimport { APIResponsePaging } from '@r/api-client';\nconst { afterResponse } = APIResponsePaging;\n\nconst options = { subredditName: 'reactjs' };\n\nconst firstPage = await PostsEndpoint.get(APIOptions, options);\nconst after = afterResponse(firstPage);\nconst withNextPage = firstPage.appendResponse(await PostsEndpoint.get(APIOptions, { ...options, after });\n```\n\nNote: instances of `MergedResponses` Dont' have `.query` and `.meta` instance variables, instead they have `.querys` and `.metas` that are lists of those from their merged responses. Merging is simple loop that when given a list of responses, takes all of the top level results (with duplicates removed) and updates the tables (e.g. `apiResponse.links`) to use the latest version of the response object. This is useful for cases like paging through subreddits and the posts near page boundaries get listed twice, but you want the most up to date score, number of comments, etc`\n\n### Smart Models\nThis directory contains models that are built to easy interacting with the api. Models will have methods like `subreddit.subscribe()` or `comment.upvote()`. Implementation wise they'll extend models and add various static and instance methods.\n\n### Collections\nCollections are used to simplyify fetching groups of things. For now all collections subclass [Listing](/collections/Listing.es6.js) has numersous helper methods for pagingation (`.withNextPage()`, `.withPreviousPage()`). Here's some documentation on the various subclasses\n\n#### [SubredditLists](/collections/SubredditLists)\n\n```javascript\nimport { optionsWithAuth } from '@r/api-client';\nimport { collections } from '@r/api-client';\nconst { SubscribedSubreddits, ModeratingSubreddits } = collections;\nconst authedOptions = optionsWithAuth('123-xgy-secret');\n\nconst subscribedSubreddits = await SubscribedSubreddits.fetch(authedOptions);\nconsole.log(subscribedSubreddits.subreddits.map(subreddit =\u003e subreddit.url));\n\nconst moderatedSubreddits = await ModeratingSubreddits.fetch(authedOptions);\nconsole.log(moderatedSubreddits.subreddits.map(subreddit =\u003e subreddit.url));\n```\n\nIn these examples `.fetch(api)` handles fetching all the pages by default. This is pending feedback.\n\n#### [PostsFromSubreddit](/collections/PostsFromSubreddit.es6.js)\n\nFor example, you can fetch all the posts in a subreddit like so:\n```javascript\nimport APIOptions from '@r/api-client';\nimport { collections } from '@r/api-client';\nconst { PostsFromSubreddit } = collections;\n\nconst frontpagePopular = await PostsFromSubreddit.fetch(APIOptions, 'all')\nconsole.log(frontpagePopular.posts.map(post =\u003e post.title);\nconst nextPage = await frontpagePopular.nextPage(APIOptions)\n```\n\nThese endpoints are designed to take options like paging. This makes it easy to do things like continue a infinite scroll after page reloads.\n```javascript\nimport APIOptions from '@r/api-client';\nimport { collections } from '@r/api-client';\nconst { PostsFromSubreddit } = collections;\n\nimport { last } from 'lodash/array';\nimport { each } from 'lodash/collection';\n\nlet frontpage = await PostsFromSubreddit.fetch(APIOptions, 'all') // blank fetches frontpage;\neach(Array(10), async () =\u003e {\n  frontpage = await frontpage.withNextPage(APIOptions);\n});\n\nconst after = last(frontpage.apiResponse.results).uuid;\nconst pageAfter = await PostsFromSubreddit.fetch(APIOptions, 'all', { after })\n```\n\nThere are lots of other endpoints you can use too. Just note in the future you'll most likely pass an object with your api options instead of an api instance. This makes more sense in a redux world, and will allow us to build the api into modules which can be imported piecemeal, which could drastically reduce payload size.\n\n#### [SavedPostsAndComments](/collections/SavedPostsAndComments.es6.js)\n```javascript\nimport { optionsWithAuth } from '@r/api-client';\nimport { collections } from '@r/api-client';\nconst { SavedPostsAndComments } = collections;\n\nconst authedOptions = optionsWithAuth('123-xgy-secret');\n\nconst savedThings = await SavedPostsAndComments.fetch(authedOptions, 'my-user-name');\nsavedThings.postsAndComments;\nconst savedWithNextPage = await savedThings.withNextPage(authedOptions);\n```\n\n#### [HiddenPostsAndComments](/collections/HiddenPostsAndComments.es6.js)\n```javascript\nimport { optionsWithAuth } from '@r/api-client';\nimport { collections } from '@r/api-client';\nconst { HiddenPostsAndComments } = collections;\n\nconst authedOptions = optionsWithAuth('123-xgy-secret');\n\nconst lessThanDankMemes = await HiddenPostsAndComments.fetch(authedOptions, 'my-user-name');\nlessThanDankMemes.postsAndComments;\n```\n\n#### [CommentsPage](/collections/CommentsPage.es6.js)\n```javascript\nimport APIOptions from '@r/api-client';\nimport { collections } from '@r/api-client';\nconst { PostsFromSubreddit } = collections;\n\nconst askRedditPosts = await PostsFromSubreddit.fetch(APIOptions, 'askreddit');\nconst post = askRedditPosts.apiResponse.uuid;\nconst commentsPage = await CommentsPage.fetch(api, post);\n```\n\n#### [SearchQuery](/collections/SearchQuery.es6.js)\n```javascript\nimport APIOptions from '@r/api-client';\nimport { collections } from '@r/api-client';\nconst { SearchQuery } = collections;\n\nconst searchResults = await SearchQuery.fetchPostsAndSubreddits(APIOptions, 'high quality gifs');\nsearchResults.posts;\nsearchResults.subreddits;\n```\n\n### Development / Testing\n\nIf you `chmod +x ./repl`, you can start up a repl for testing (and for general\nuse!) An api instance is created in the global scope (`api`), from which you\ncan call any of the API methods. Use `help` in the repl to learn more.\n\nIf you want to use your account to see thigns like your subscriptions, saved,\netc: ./repl will use the environment variable 'TOKEN' if supplied.\n\n```bash\nexport TOKEN='my-super-secret-secure-oauth-token'\n./repl\n```\n```javascript\napi.saved.get({ user: 'my-user-name' }).then(console.log)\n```\n\n\nMancy [Not recommended, this is outdated and won't be updated for now]\n-----\n\nIf you install [Mancy](https://github.com/princejwesley/Mancy) you can have a nicer version of using the repl. To set it up, open mancy, and go to `Preferences`. Under `Add node modules path` add your local install of Snoode. Then under `Startup script` add `mancyStart.js`. You can edit `mancyStart.js` to include your token and you can then either use `api` or `authed` as you'd expect. Mancy supports lots of inspecting and autocomplete tools out of the box.\n\nExample `mancyStart.js`:\n```\nvar api = require('mancyLoader.js')\nvar authed = api.withConfig({\n    token: \"\u003cYOUR_TOKEN_HERE\u003e\",\n    origin: \"https://oauth.reddit.com\"})\n```\n\n\nCaveats\n------\n\nWe write ES6/7 and compile via Reddit's build system [@r/build](https://www.github.com/reddit/node-build). We output a built file that expects a polyfilled es6/7 environment, with a lodash and superagent as peer depedencies. In your project you'll have to include those as depedencies and `import 'babel-polyfill'` or `require('babel-polyfill')` before using the api.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freddit%2Fnode-api-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freddit%2Fnode-api-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freddit%2Fnode-api-client/lists"}