{"id":13527766,"url":"https://github.com/phantombuster/nickjs","last_synced_at":"2025-04-01T09:32:07.807Z","repository":{"id":71712326,"uuid":"76658683","full_name":"phantombuster/nickjs","owner":"phantombuster","description":"Web scraping library made by the Phantombuster team. Modern, simple \u0026 works on all websites. (Deprecated)","archived":true,"fork":false,"pushed_at":"2020-06-19T17:07:10.000Z","size":446,"stargazers_count":501,"open_issues_count":15,"forks_count":48,"subscribers_count":30,"default_branch":"master","last_synced_at":"2024-11-02T12:35:08.445Z","etag":null,"topics":["automation","browser","casperjs","deprecated","headless-chrome","phantomjs","scraping"],"latest_commit_sha":null,"homepage":"https://nickjs.org","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/phantombuster.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}},"created_at":"2016-12-16T14:11:01.000Z","updated_at":"2024-10-28T01:26:21.000Z","dependencies_parsed_at":"2023-03-01T05:30:20.658Z","dependency_job_id":null,"html_url":"https://github.com/phantombuster/nickjs","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phantombuster%2Fnickjs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phantombuster%2Fnickjs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phantombuster%2Fnickjs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phantombuster%2Fnickjs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/phantombuster","download_url":"https://codeload.github.com/phantombuster/nickjs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246616488,"owners_count":20806148,"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":["automation","browser","casperjs","deprecated","headless-chrome","phantomjs","scraping"],"created_at":"2024-08-01T06:02:00.613Z","updated_at":"2025-04-01T09:32:07.102Z","avatar_url":"https://github.com/phantombuster.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"http://nickjs.org/\"\u003e\n    \u003cimg alt=\"NickJS\" src=\"https://raw.githubusercontent.com/phantombuster/nickjs/master/logo.png\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\u003chr/\u003e\n\u003cp align=\"center\"\u003e\n  ⛔ This project is deprecated, please consider using \u003ca href=\"https://github.com/puppeteer/puppeteer\"\u003ePuppeteer\u003c/a\u003e instead. ⛔\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  NickJS predates Puppeteer and is no longer the best tool around. This project isn't maintained anymore.\n\u003c/p\u003e\n\u003chr/\u003e\n\n\u003cp align=\"center\"\u003e\n  Web scraping library made by the \u003ca href=\"https://phantombuster.com\"\u003ePhantombuster\u003c/a\u003e team. Modern, simple \u0026 works on all websites.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/nickjs\"\u003e\u003cimg alt=\"NPM version\" src=\"https://img.shields.io/npm/v/nickjs.svg?style=flat-square\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://gitter.im/phantombuster/nickjs\"\u003e\u003cimg alt=\"Gitter room\" src=\"https://img.shields.io/gitter/room/Phantombuster/Lobby.svg?style=flat-square\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://twitter.com/phbuster\"\u003e\u003cimg alt=\"Twitter follow\" src=\"https://img.shields.io/twitter/follow/phbuster.svg?style=social\u0026label=Follow\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"http://nickjs.org\"\u003eNickJS.org\u003c/a\u003e — \u003ca href=\"https://github.com/phantombuster/nickjs#documentation\"\u003eInline doc ↓\u003c/a\u003e\n\u003c/p\u003e\n\n* Supports both Headless Chrome and PhantomJS as drivers\n* Simple high-level API\n* Async/await, Promises and callback coding styles\n\nNickJS allows you to automate navigation and collect data from any website. By controlling an instance of either [Headless Chrome](https://developers.google.com/web/updates/2017/04/headless-chrome) or [PhantomJS with CasperJS](http://casperjs.org/), your bots will simulate a human.\n\nIt's simple and allows for an easy implementation of our [3 scraping steps theory](https://blog.phantombuster.com/were-making-web-scraping-so-easy-that-you-re-going-to-love-it-d3efe3a3fad4).\n\n# Example code\n\n```javascript\nconst Nick = require(\"nickjs\")\nconst nick = new Nick()\n\n;(async () =\u003e {\n\n\tconst tab = await nick.newTab()\n\tawait tab.open(\"news.ycombinator.com\")\n\n\tawait tab.untilVisible(\"#hnmain\") // Make sure we have loaded the page\n\n\tawait tab.inject(\"http://code.jquery.com/jquery-3.2.1.min.js\") // We're going to use jQuery to scrape\n\tconst hackerNewsLinks = await tab.evaluate((arg, callback) =\u003e {\n\t\t// Here we're in the page context. It's like being in your browser's inspector tool\n\t\tconst data = []\n\t\t$(\".athing\").each((index, element) =\u003e {\n\t\t\tdata.push({\n\t\t\t\ttitle: $(element).find(\".storylink\").text(),\n\t\t\t\turl: $(element).find(\".storylink\").attr(\"href\")\n\t\t\t})\n\t\t})\n\t\tcallback(null, data)\n\t})\n\n\tconsole.log(JSON.stringify(hackerNewsLinks, null, 2))\n\n})()\n.then(() =\u003e {\n\tconsole.log(\"Job done!\")\n\tnick.exit()\n})\n.catch((err) =\u003e {\n\tconsole.log(`Something went wrong: ${err}`)\n\tnick.exit(1)\n})\n```\n\n# Usage\n\nFirst of all, install NickJS: `npm install nickjs`.\n\nNickJS will choose which headless browser to use depending on how you launch it. When launching your script with `node`, Headless Chrome will be used. When launched with `casperjs`, CasperJS+PhantomJS will be used.\n\nTo get started with the PhantomJS driver, [read this](PHANTOMJS.md). However we recommend using Headless Chrome (read on).\n\nYou'll need to have Node 7+ and Chrome 63+ installed on your system (read the next section for more info about which Chrome version you should use). The path to the Chrome executable can be specified with `export CHROME_PATH=/path/to/chrome` otherwise the binary `google-chrome-beta` will be used.\n\nLaunching a bot is then as simple as `node my_nickjs_script.js`.\n\n## Headless Chrome version\n\nNickJS makes use of the latest DevTools protocol methods, so you'll need a very recent version of Chrome.\n\nAt the time of writing, NickJS is using some methods from Chrome 63, which is the Beta Channel. Having the correct version of Chrome is critical for a smooth experience with NickJS. Go to the [Chrome Release Channels](https://www.chromium.org/getting-involved/dev-channel) page and download a version compatible with your system. If you want this to be taken care of for you, check out [Phantombuster](https://phantombuster.com), which is basically our \"NickJS as a service\" platform.\n\n## Environment variables\n\nThe following environment variables have an effect on NickJS:\n\n- `CHROME_PATH`: **specifies where to find the Google Chrome binary — this is important!** Example: `\"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome\"`\n- `NICKJS_LOAD_IMAGES` (0 or 1): disables image loading (equivalent to NickJS' constructor option `loadImages`)\n- `NICKJS_NO_SANDBOX` (0 or 1): disables Chrome's sandboxing (no effect when the CasperJS+PhantomJS driver is used)\n- `NICKJS_PROXY` or `http_proxy`: see below\n\n# HTTP proxy\n\nNickJS supports HTTP (and HTTPS) proxies. Other protocols are not yet supported. To specify which proxy to use, set the `httpProxy` option in [NickJS' constructor](https://hub.phantombuster.com/v1/reference#nick). You can also set the environment variable `NICKJS_PROXY` or the standard `http_proxy` (but the constructor option takes precedence).\n\nYour proxy must be specified in the following format: `http://username:password@proxy.com:3128` (the protocol portion is optional).\n\nContrary to some other libraries, yes, **NickJS supports proxy authentication with Headless Chrome**.\n\n# Documentation\n\n+ [Nick](#nick)\n  + [Nick()](#nickoptions)\n  + [deleteAllCookies()](#deleteallcookiescallback)\n  + [deleteCookie()](#deletecookiecookiename-cookiedomain-callback)\n  + [driver](#driver)\n  + [exit()](#exitcode)\n  + [getAllCookies()](#getallcookiescallback)\n  + [newTab()](#newtab)\n  + [setCookie()](#setcookiecookie-callback)\n+ [Nick tab](#nick-tab)\n  + [click()](#clickselector-callback)\n  + [close()](#closecallback)\n  + [evaluate()](#evaluateinpagefunction--argumentobject-callback)\n  + [fill()](#fillselector-inputs--submit-callback)\n  + [getContent()](#getcontentcallback)\n  + [getUrl()](#geturlcallback)\n  + [inject()](#injecturlorpath--callback)\n  + [isPresent()](#ispresentselectors-conditions-callback)\n  + [isVisible()](#isvisibleselectors-conditions-callback)\n  + [onConfirm](#onconfirm)\n  + [onPrompt](#onprompt)\n  + [open()](#openurl--options-callback)\n  + [screenshot()](#screenshotfilename--callback)\n  + [scroll()](#scrollx-y,-callback)\n  + [scrollToBottom()](#scrolltobottomcallback)\n  + [sendKeys()](#sendkeysselector-keys-options-callback)\n  + [wait()](#waitduration-callback)\n  + [waitUntilPresent()](#waituntilpresentselectors--timeout--condition-callback)\n  + [waitUntilVisible()](#waituntilvisibleselectors--timeout--condition-callback)\n  + [waitWhilePresent()](#waitwhilepresentselectors--timeout--condition-callback)\n  + [waitWhileVisible()](#waitwhilevisibleselectors--timeout--condition-callback)\n\n\n\n# Nick\n\n\n# Nick([options])\n**This is Nick's constructor. `options` is an optional argument that lets you configure your Nick instance.**\n\n\n\nNick must be instantiated only once. Behind the scenes, the headless browser driver is initialized. The next step is to open a tab with [newTab()](https://hub.phantombuster.com/v1/reference#nick-newtab).\n\n### — [options] `(PlainObject)`\n\nOptional settings for the Nick instance.\n* **`printNavigation (Boolean)`**: when `true` (the default), Nick will log important navigation information like page changes, redirections and form submissions\n* **`printResourceErrors (Boolean)`**: when `true` (the default), Nick will log all the errors encountered when loading pages, images and all other resources needed by the pages you visit\n* **`printPageErrors (Boolean)`**: when `true` (the default), Nick will log all JavaScript errors and exceptions coming from the scripts executed in the page context\n* **`resourceTimeout (Number)`**: milliseconds after which Nick will abort loading a resource (page, images and all other resources needed by the pages you visit)\n* **`userAgent (String)`**: sets the `User-Agent` header\n* **`loadImages (Boolean)`**: whether or not to load the images embedded in the pages (defaults to `true`) (note: specifying this parameter overrides the agent's Phantombuster setting \"Load Images\")\n* **`blacklist (Array)`**: soon!\n* **`whitelist (Array)`**: soon!\n* **`childStdout (String)`**: when `stderr` can redirect stdout to stderr\n* **`childStdout (String)`**: when `stdout` can redirect stderr to stdout\n* **`additionalChildOptions (Array)`**: When chrome is used this is an Array of string (e.g `[\"--ignore-certificate-errors\", \"--ignore-urlfetcher-cert-requests\"]`), for CasperJs though this is an array of objects (e.g `[{verbose: true, logLevel: \"debug\" }]`)\n\n##### Basic (ES6+)\n```javascript\nconst Nick = require(\"nickjs\")\nconst nick = new Nick()\n```\n\n##### All options (ES6+)\n```javascript\nconst Nick = require(\"nickjs\")\n\n// these are the default options\nconst nick = new Nick({\n  printNavigation: true,\n  printResourceErrors: true,\n  printPageErrors: true,\n  timeout: 10000,\n  userAgent: \"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36\"\n})\n```\n\n# deleteAllCookies([callback])\n**Deletes all cookies set to the headless browser.**\n\n### — callback `(Function)`\n\nFunction called when finished(*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if something went wrong\n\n\n\n##### Example\n```javascript\ntry {\n  await nick.deleteAllCookies()\n  // All cookies are cleanded up\n} catch (err) {\n  console.log(\"Could not delete all cookies:\", err)\n}\n```\n\n##### :no_entry_sign: Warning\n\u003e This method will delete all cookies that might be necessary to your bot.\n\n# deleteCookie(cookieName, cookieDomain[, callback])\n**Deletes a specific cookie set in the headless browser.**\n\n### — callback `(Function)`\n\nFunction called when finished(*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if something went wrong.\n\n##### Example\n```javascript\nconst cookieName = \"cookieName\"\nconst cookieDomain = \".domain.com\"\n\ntry {\n  await nick.deleteCookie(cookieName, cookieDomain)\n} catch (err) {\n  console.log(\"Could not delete cookie:\", err)\n}\n```\n\n# driver\n`nick.driver` lets you access the underlying headless browser driver instance that is being used by Nick.\n\nThis is useful when doing trickier things in your navigation and for accessing driver-specific methods that are not available in Nick.\n\n##### PhantomJS+CasperJS driver\n```javascript\n// In this case we're using the PhantomJS+CasperJS driver\n// This gets the CasperJS instance and clears the cache\nnick.driver.casper.page.clearMemoryCache()\n```\n\n# exit([code])\n**Immediately stops the whole bot and exits the process with `code`.**\n\n### — [code] `(Number)`\n\nOptional exit code that the process should return. `0` by default.\n\n\n\n##### Example 1\n```javascript\nnick.exit() // All is well\n```\n\n##### Example 2\n```javascript\nnick.exit(1) // Something went horribly wrong\n```\n\n# getAllCookies([callback])\n**Gets an object containing all cookies set in the headless browser.**\n\n### — callback `(Function)`\n\nFunction called when finished(*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if something went wrong.\n* **`cookies (PlainObject)`**: an object containing all cookies of the headless browser and their properties\n\n##### Example\n```javascript\ntry {\n  const cookies = await nick.getAllCookies()\n  // Cookies contain all your cookies\n  console.log(cookies, null, 2)\n} catch (err) {\n  console.log(\"Could not get all cookies:\", err)\n}\n```\n\n# newTab()\n**Opens a new tab.**\n\nThis is the first step in manipulating a website.\n\nTo open multiple tabs, call this method multiple times. If your bot opens many tabs to do different tasks, it's a good idea to [close()](https://hub.phantombuster.com/v1/reference#nick-close) them when their work is finished (to keep memory usage down).\n\n##### Example\n```javascript\ntry {\n  const tab = await nick.newTab()\n  // You can now browse any website using `tab`\n} catch (err) {\n  console.log(\"An error occured:\", err)\n}\n```\n\n# setCookie(cookie[, callback])\n**Sets a cookie.**\n\nSet the name, the value and the domain of a cookie.\nThis cookie can be seen with [getAllCookies()](ref:getallcookies)  and deleted with [deleteAllCookies()](ref:deleteallcookies) or [deleteCookie()](ref:deletecookie).\n\n\n### — cookie `(PlainObject)`\n\nAn object containing all attributes of a cookie.\n\n* **`name (String)`**: Name of the cookie you want to set.\n* **`value (String)`**: Value of the cookie you want to set.\n* **`domain (String)`**: Domain linked to the cookie set.\n\n### — callback `(Function)`\n\nFunction called when finished(*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if something went wrong.\n\n##### Example\n```javascript\nconst cookie = {\n  name: \"cookieName\",\n  value: \"cookieValue\",\n  domain: \".domain.com\"\n}\n\ntry {\n  await nick.setCookie(cookie)\n  // You can navigate with your cookie set\n} catch (err) {\n  console.log(\"Could not create cookie:\", err)\n}\n```\n\n# Nick-tab\n\n\n# click(selector[, callback])\n**Performs a click on the element matching the CSS selector `selector`.**\n\nClicking on elements is one of the main ways to manipulate web pages with Nick. Clicking is an easy way to navigate where you want, but keep in mind that it can be more efficient to scrape URLs (for example with [`evaluate()`](https://hub.phantombuster.com/v1/reference#nick-evaluate)) and then call [`open()`](https://hub.phantombuster.com/v1/reference#nick-open).\n\n### — selector `(String)`\n\nCSS selector targeting what element to click.\nProbably a `button` or an `a` but can be anything you want.\nMake sure the target element is visible or present by calling [`waitUntilVisible()`](https://hub.phantombuster.com/v1/reference#nick-waituntilvisible) or [`waitUntilPresent()`](https://hub.phantombuster.com/v1/reference#nick-waituntilpresent) beforehand.\n\n### — callback `(Function(err))`\n\nFunction called when finished (*optional*).\n* **`err (String)`**: `null` or a string describing what went wrong with the click (typically the CSS selector did no match any element)\n\n\n\n##### Example\n```javascript\nconst selector = \"button.cool-button\"\nconst pageTimeout = 5000\n\ntry {\n  await tab.waitUntilPresent(selector, pageTimeout)\n  await tab.click(selector)\n} catch (err) {\n  console.log(\"An error occured:\", err)\n}\n// Continue your navigation in this branch\n// You should probably do a waitUntilVisible() or waitUntilPresent() here\n```\n\n##### :warning: Make sure your target is here\n\u003e Before calling `click()` you should make sure the element you are trying to click on is actually visible or present in the page by using [`waitUntilVisible()`](https://hub.phantombuster.com/v1/reference#nick-waituntilvisible) or [`waitUntilPresent()`](https://hub.phantombuster.com/v1/reference#nick-waituntilpresent).\n\n# close([callback])\n**Closes the `tab` in current use.**\n\nAfter `close()` is called, the tab becomes unusable.\nAll subsequent method calls will throw an exception saying that this specific tab instance has stopped.\nLose all references to the instance for it to be garbage-collected and clean cookies and cache used for the whole nick.\n\n\n### — callback `(Function)`\n\nFunction called when finished (*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if something went wrong\n\n\n\n\n\n\n\n##### Example\n```javascript\ntry {\n  await tab.close()\n  // tab can not be used here anymore\n  // but you may continue other actions\n} catch (err) {\n  console.log(\"Could not close tab:\", err)\n}\n```\n\n##### :information_source: It's like closing a tab in your browser\n\u003e This method is useful when using multiple Nick instances to simulate browsing on multiple tabs. Calling `close()` is the equivalent of closing a tab.\n\n##### :information_source: Tips\n\u003e It can be also useful if you want to iterate on many URLs, the fact that close() clear cache and cookies free a lot of memory.\n\n##### :no_entry_sign: Warning\n\u003e Calling `close()` will clear the cookies and cache of the **whole `nick`** instantiated before.\n\n# evaluate(inPageFunction [, argumentObject, callback])\n**Executes `inPageFunction` in the current page context.**\n\nNick provides you with **two separate JavaScript contexts**:\n1. **Where the Nick code runs**: this is your script environment, with all your locally declared variables and all your calls to Nick methods\n2. **Where the page code runs**: this is where the page executes jQuery or AngularJS code for example\n\nThe `evaluate()` method allows you to declare a function in your Nick context (1) and executes it in the page context (2). **It's like executing code in your browser's inspector tool**: you can do anything you want with the page.\n\nIn the page context, you have access to all the global variables declared by the page, as well as the DOM (`window`, `document`, ...). Any JavaScript libraries included by the page can also be used.\n\nIf the page does not include what you want (jQuery or underscore for example), you can inject any JavaScript file with [`inject()`](https://hub.phantombuster.com/v1/reference#nick-inject) before calling `evaluate()`.\n\n### — inPageFunction `(Function(argumentObject, callback))`\n\nFunction to execute in the current page context. `argumentObject` will be passed as its first argument and a `callback` as it second argument.\n`argumentObject` is an empty plainObject by default.\n`callback` is the function to call when finished.\n* **`err (String)`**: `null` if the function succeeds otherwise put a description of what went wrong\n* **`res (Any)`**: return value of `inPageFunction` in case of success (this value is serialized to be transferred back to the Nick context — complex object like DOM elements, functions or jQuery objects cannot be returned to the Nick context reliably)\n\n### — [argumentObject] `(PlainObject)`\n\nOptional object that will be passed as an argument of `inPageFunction` (*optional*).\nThis object is serialized to be transferred to the page context — complex objects like functions or JavaScript modules cannot be passed as argument reliably.\n\n### — callback `(Function(err, res)`\n\nFunction called when finished.\n* **`err (String)`**: `null` or a string describing what went wrong during the evaluation of `inPageFunction`\n* **`res (Any)`**: return value of `inPageFunction` in case of success (this value is serialized to be transferred back to the Nick context — complex object like DOM elements, functions or jQuery objects cannot be returned to the Nick context reliably)\n\n\n\n\n\n\n\n##### Example\n```javascript\nconst scraper = (arg, done) =\u003e {\n  // In this case, the current page uses a typical jQuery declared as $\n  done(null, $(arg.link).attr(\"href\"))\n}\nconst arg = { link: \"#header \u003e a.title\" }\n\ntry {\n  const res = await tab.evaluate(scraper, arg)\n  console.log(\"Scraped this link:\", res)\n  // Continue your navigation here\n} catch (err) {\n  console.log(\"Something went wrong:\", err)\n}\n```\n\n##### :no_entry_sign: Local variables not accessible\n\u003e Because `inPageFunction` is executed in the current page context, your local variables that have been declared before your `evaluate()` call will **not** be accessible. You can, however, transfer variables using the `argumentObject` parameter.\n\n**For this reason, Nick methods won't be available inside evaluate.**\n\n##### :no_entry_sign: Error in callback\n\u003e When returning data with the callback in the `inPageFunction` take care to always set the first argument as `null` if there is no error.\n\n##### :warning: Serialization subtleties\n\u003e Keep in mind that to transfer `inPageFunction` and its return value to and from the page context, serialization has to occur. Everything becomes a string at some point. **So you cannot return DOM elements or jQuery objects from the page.** Moreover, the underlying PhantomJS browser has [a bug](https://github.com/ariya/phantomjs/issues/11268) where serialization of `null` gives an empty string `\"\"` (even in nested objects and arrays). Beware!\n\n# fill(selector, inputs [, submit, callback])\n**Fills a form with the given values and optionally submits it.**\n\n Inputs are referenced by their name attribute.\n\n### — selector `(String)`\n\nCSS selector targeting what form to fill. It should point to a `form` tag. Make sure the target form is visible or present by calling [`waitUntilVisible()`](https://hub.phantombuster.com/v1/reference#nick-waituntilvisible) or [`waitUntilPresent()`](https://hub.phantombuster.com/v1/reference#nick-waituntilpresent) beforehand.\n\n### — inputs `(PlainObject)`\n\nAn object containing the data you want to enter in the form.\n**Keys must correspond to the inputs' `name` attribute.** This method supports single `select` fields in the same way as normal `input` fields. For `select` fields allowing multiple selections, supply an array of values to match against.\n\n### — options `(Boolean)`\n\n* **`submit (Boolean)`**: Whether or not to submit the form after filling it (`false` by default).\n\n### — callback `(Function(err))`\n\nFunction called when finished.\n* **`err (String)`**: `null` or a string describing what went wrong when filling the form\n\n\n\n##### Example\n```javascript\nconst selector = \"#contact-form\"\nconst inputs = {\n  \"subject\": \"I am watching you\",\n  \"content\": \"So be careful.\",\n  \"civility\": \"Mr\",\n  \"name\": \"Chuck Norris\",\n  \"email\": \"chuck@norris.com\",\n  \"cc\": true,\n  \"attachment\": \"roundhousekick.doc\" // file taken from your agent's disk\n}\n\ntry {\n  await tab.waitUntilVisible(selector, 5000)\n  await tab.fill(selector, inputs, { submit: true })\n  console.log(\"Form sent!\")\n  // Continue your navigation in this branch\n  // You should probably do a waitUntilVisible() or waitUntilPresent() here\n} catch (err) {\n  console.log(\"Form not found:\", err)\n}\n```\n\n##### Form used in the example (HTML)\n```html\n\u003cform action=\"/contact\" id=\"contact-form\" enctype=\"multipart/form-data\"\u003e\n  \u003cinput type=\"text\" name=\"subject\"/\u003e\n  \u003ctextearea name=\"content\"\u003e\u003c/textearea\u003e\n  \u003cinput type=\"radio\" name=\"civility\" value=\"Mr\"/\u003e Mr\n  \u003cinput type=\"radio\" name=\"civility\" value=\"Mrs\"/\u003e Mrs\n  \u003cinput type=\"text\" name=\"name\"/\u003e\n  \u003cinput type=\"email\" name=\"email\"/\u003e\n  \u003cinput type=\"file\" name=\"attachment\"/\u003e\n  \u003cinput type=\"checkbox\" name=\"cc\"/\u003e Receive a copy\n  \u003cinput type=\"submit\"/\u003e\n\u003c/form\u003e\n```\n\n# getContent([callback])\n**Returns the current page content as a string.**\n\n### — callback `(Function(err))`\n\nFunction called when finished (*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if something went wrong\n* **`content (String)`**: the full HTML content of the current webpage.\n\n\n\n\n##### Example\n```javascript\ntry {\n  const content = await tab.getContent()\n  // content contains the content of the current webpage\n} catch (err) {\n  console.log(\"Could not get the content of the page:\", err)\n}\n```\n\n##### :warning: Note\n\u003e When the current page is a dynamic JavaScript powered HTML page, `getContent()` will return a snapshot of the current state of the DOM and not the initial source code.\n\n# getUrl([callback])\n**Returns the current page URL as a string.**\n\n### — callback `(Function(err))`\n\nFunction called when finished (*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if something went wrong\n* **`url (String)`**: the full `URL` of the current page.\n\n\n\n##### Example\n```javascript\ntry {\n  const url = await tab.getUrl()\n  console.log(\"The url of the page is\", url)\n  // You can use the variable url and continue your actions\n} catch (err) {\n  console.log(\"Could not get the current url:\", err)\n}\n```\n\n##### :information_source: Note\n\u003e The URL you get will be URL-decoded.\n\n# inject(urlOrPath [, callback])\n**Injects a script in the current DOM page context.**\n\nThe script can be stored locally on disk or on a remote server.\n\n### — urlOrPath `(String)`\n\nPath to a local or remote script.\n\n### — callback `(Function(err))`\n\nFunction called when finished (*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if something went wrong\n\n##### Example\n```javascript\nconst urlOrPath = \"https://code.jquery.com/jquery-3.2.1.min.js\"\n\ntry {\n  await tab.inject(urlOrPath)\n  console.log(\"Jquery script inserted!\")\n  //You may now use tab.evaluate() and use jQuery functions\n} catch (err) {\n  console.log(\"Could not inject jQuery:\", err)\n}\n```\n\n# isPresent(selectors[, conditions, callback])\n**Checks for a list of `selectors` CSS selectors if they are present in the DOM and return a boolean: `true` if the selectors are present and `false` in the contrary.**\n\n### — selectors `(Array or String)`\n\nWhat to look for in the DOM. Can be an array of CSS selectors (array of strings) or a single CSS selector (string).\n\n### — [condition] `(String)`\n\nWhen `selectors` is an array, this optional argument lets you choose how to wait for the CSS selectors(*optional*).\nIf `condition` is `\"and\"` (the default), the method will check for the presence of **all** CSS selectors.\nOn the other hand, if `condition` is `\"or\"`, the method will check for the presence of **any** CSS selector.\n\n### — callback `(Function(err, selector))`\n\nFunction called when finished (*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if the function fails to check\n* **`visible (Boolean)`**: `true` if the condition succeeds or `false` in the contrary\n##### Example\n```javascript\nconst selectors = [\"div.first\", \"div.second\"]\n\nconst present = await tab.isPresent(selectors, \"or\")\nif (present) {\n  // Either .first or .second is present at this time\n} else {\n  console.log(\"Elements aren't present\")\n}\n```\n\n# isVisible(selectors[, conditions, callback])\n**Checks for a list of `selectors` CSS selectors if they are visible in the page and return a boolean: `true` if the selectors are visible and `false` in the contrary.**\n\n### — selectors `(Array or String)`\n\nWhat to check for. Can be an array of CSS selectors (array of strings) or a single CSS selector (string).\n\n### — [condition] `(String)`\n\nWhen `selectors` is an array, this optional argument lets you choose how to wait for the CSS selectors (*optional*).\nIf `condition` is `\"and\"` (the default), the method will check for the visibility of **all** CSS selectors.\nOn the other hand, if `condition` is `\"or\"`, the method will check for the visibility of **any** CSS selector.\n\n### — callback `(Function(err, selector))`\n\nFunction called when finished (*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if the function fails to check\n* **`visible (Boolean)`**: `true` if the condition succeeds or `false` in the contrary\n##### Example\n```javascript\nconst selectors = [\"div.first\", \"div.second\"]\n\nconst visible = await tab.isVisible(selectors, \"or\")\nif (visible) {\n  // Either .first or .second is visible at this time\n} else {\n  console.log(\"Elements aren't visible\")\n}\n```\n\n# onConfirm\n**Sets an event to a JS confirm alert.**\nExecutes the function assigned to this variable whenever a confirm dialog is called by `window.confirm`.\nThe only parameter is the message sent by the dialog, and the function needs to return the user's response as a boolean.\n\n### — message `(String)`\nA string containing the message from the confirm dialog.\n\n##### ES6+\n```javascript\ntab.onConfirm = (message) =\u003e {\n  console.log(\"The confirm messsage is\", message)\n  return true\n}\n```\n\n# onPrompt\n**Sets an event to a JS prompt alert.**\nExecutes the function assigned to this variable whenever a prompt dialog is called by `window.prompt()`.\nThe only parameter is the message sent by the dialog, and the function needs to return the user's response as a string.\n\n### — message `(String)`\nA string containing the message from the prompt dialog.\n\n##### ES6+\n```javascript\ntab.onPrompt = (message) =\u003e {\n  console.log(\"The prompt message is\", message)\n  return \"Response\"\n}\n```\n\n# open(url [, options, callback])\n**Opens the webpage at `url`.**\n\nBy default, it's a `GET` but you can forge any type of HTTP request using the `options` parameter.\n\nOpening a page will time out after 10 seconds. This can be changed with the `resourceTimeout` Nick option (see [Nick's options](https://hub.phantombuster.com/v1/reference#nick)). Note: this time out concerns the initial page but not the resources the page requires thereafter.\n\n### — url `(String)`\n\nURL of the page to open. Should begin with `http://` or `https://` (or `file://` to open a page that was previously downloaded to your agent's disk).\n\n### — [options] `(PlainObject)`\n\nOptional request configuration (*optional*).\n\n### — callback `(Function(err, httpCode, httpStatus, url))`\n\nFunction called when finished (*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if something went wrong (typically if there was a network error or timeout)\n* **`httpCode (Number)`**: the received HTTP code or `null` if there was a network error\n* **`httpStatus (String)`**: text equivalent of the received HTTP code or `null` if there was a network error\n* **`url (String)`**: the actually opened URL (can be different from the input URL because of 3xx redirects for example) or `null` if there was a network error\n\n\n\n##### Example\n```javascript\nconst url = \"https://phantombuster.com/\"\n\ntry {\n  const [httpCode, httpStatus] = await tab.open(url)\n\n  if ((httpCode \u003e= 300) || (httpCode \u003c 200)) {\n    console.log(\"The site responded with\", httpCode, httpStatus)\n  } else {\n    console.log(\"Successfully opened\", url, \":\", httpCode, httpStatus)\n    // Manipulate the page in this branch\n    // You should probably do a waitUntilVisible() or waitUntilPresent() here\n  }\n} catch(err) {\n  console.log(\"Could not open page:\", err)\n}\n\n```\n\n##### JavaScript: sample options\n```javascript\n{\n  method: \"post\",\n  data:   {\n    \"some param\": \"some data\",\n    \"another field\":  \"this is sent in x-www-form-urlencoded format\"\n  },\n  headers: {\n    \"Accept-Language\": \"fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3\"\n  }\n}\n```\n\n##### :no_entry_sign: Know your errors\n\u003e This method will NOT return an error when the received HTTP isn't 200. An error is returned only when a network error happens. It's your job to check for 404s or 500s with `httpCode` if needed.\n\n##### :warning: Always wait for DOM elements\n\u003e Many pages on the web load slowly and unreliably. Many more make numerous aynchronous queries. For these reasons, you should always wait for the DOM elements that interest you after opening a page with [`waitUntilVisible()`](https://hub.phantombuster.com/v1/reference#nick-waitUntilVisible)or [`waitUntilPresent()`](https://hub.phantombuster.com/v1/reference#nick-waituntilpresent).\n\n# screenshot(filename [, callback])\n**Takes a screenshot of the current page.**\n\n### — path `(String)`\n\nThe local path of the screenshot.\nThe format is defined by the file extension. 'image.jpg' will create a JPEG image in the current folder.\n\n### — callback `(Function(err))`\n\nFunction called when finished (*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if something went wrong\n\n##### Example\n```javascript\nconst path = \"./image.jpg\"\n\ntry {\n  await tab.screenshot(path)\n  console.log(\"Screenshot saved at\", path)\n  // Your screenshot is available at this path\n} catch (err) {\n  console.log(\"Could not take a screenshot:\", err)\n}\n```\n\n# scroll(x, y,[, callback])\n**Scrolls to coordinates `[x,y]` on the page.**\n\n### — x `(Number)`\nThe X-axis coordinate in pixels to scroll to (horizontally).\n\n### — y `(Number)`\nThe Y-axis coordinate in pixels to scroll to (vertically).\n\n### — callback `(Function(err))`\nFunction called when finished (*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if something went wrong\n\n##### Example\n```javascript\nconst x = 1000\nconst y = 2000\n\ntry {\n  await tab.scroll(x, y)\n  // Your position will be [1000, 2000] in the page now\n} catch (err) {\n  console.log(\"Could not scroll to coordinates:\", err)\n}\n```\n\n##### :information_source: Tips\n\u003e _scroll() can also be called using scrollTo()_\n\n# scrollToBottom([callback])\n**Scrolls to the bottom of the page.**\n\n### — callback `(Function(err))`\nFunction called when finished (*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if something went wrong\n##### Example\n```javascript\ntry {\n  await tab.scrollToBottom()\n  // You are now at the bottom of the page\n} catch (err) {\n  console.log(\"An error occured during the scroll to bottom:\", err)\n}\n```\n\n# sendKeys(selector, keys[, options, callback])\n**Writes `keys` in an `\u003cinput\u003e`, `\u003ctextarea\u003e` or any DOM element with `contenteditable=\"true\"` in the current page.**\n\n### — selector `(String)`\n\nA CSS3 or XPath expression that describes the path to DOM elements.\n\n### — keys `(String)`\n\nKeys to send to the editable DOM element.\n\n### — options `(String)`\n\nThe three options available are:\n\n* `reset (Boolean)`: remove the content of the targeted element before sending key presses.\n* `keepFocus (Boolean)`: keep the focus in the editable DOM element after keys have been sent (useful for input with dropdowns).\n* `modifiers (PlainObject)`: modifier string concatenated with a + (available modifiers are ctrl, alt, shift, meta and keypad).\n\n### — callback `(Function(err))`\n\nFunction called when finished(*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if something went wrong\n\n##### Example\n```javascript\nconst selector = '#message'\nconst keys = \"Boo!\"\nconst options = {\n  reset: true,\n  keepFocus: false,\n  modifiers: {}\n}\n\ntry {\n  await tab.sendKeys(selector, keys, options)\n  console.log(\"Keys sent!\")\n  // You may continue your actions here\n} catch (err) {\n  console.log(\"Could not send keys:\", err)\n}\n```\n\n# wait(duration[, callback])\n**Wait for `duration` milliseconds.**\n\n\n\n\n### — duration `(Number)`\nThe number of milliseconds to wait for.\n\n### — callback `(Function(err))`\nFunction called when finished (*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if something went wrong\n##### :no_entry_sign: Warning\n\u003e This function has nothing to do with the tab you are using, it is pure syntactic sugar to replace `Promise.delay()` (from [Bluebird](http://bluebirdjs.com/docs/api/promise.delay.html)).\nIt is like waiting in front of your computer after opening a web page.\n\n##### Example\n```javascript\ntry {\n  await tab.doSomething()\n  await tab.wait(10000)\n  // After waiting 10 seconds the script continues\n  await tab.doSomething()\n} catch (err) {\n  console.log(\"An error occured during the execution:\", err)\n}\n```\n\n# waitUntilPresent(selectors [, timeout,  condition, callback])\n**Waits for a list of `selectors` CSS selectors to be present in the DOM.**\nAborts with an error if the elements have not become present in the DOM after `timeout` milliseconds.\n\n`selectors` can be an array of CSS selectors (array of strings) or a single CSS selector (string).\n\nBy default, `condition` is `\"and\"` (wait for **all** CSS selectors) but it can be changed to `\"or\"` (wait for **any** CSS selector).\n\n### — selectors `(Array or String)`\n\nWhat to wait for. Can be an array of CSS selectors (array of strings) or a single CSS selector (string).\n\n### — timeout `(Number)`\n\nMaximum number of milliseconds to wait for, by default it is set to 5000(*optional*).\n`callback` will be called with an error if the elements have not become present after `timeout` milliseconds.\n\n### — [condition] `(String)`\n\nWhen `selectors` is an array, this optional argument lets you choose how to wait for the CSS selectors(*optional*).\nIf `condition` is `\"and\"` (the default), the method will wait for **all** CSS selectors.\nOn the other hand, if `condition` is `\"or\"`, the method will wait for **any** CSS selector.\n\n### — callback `(Function(err, selector))`\n\nFunction called when finished(*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if the CSS selectors were not present after `timeout` milliseconds\n* **`selector (String)`**:\n  * In case of success (`err` is `null`):\n    * If condition was `\"and\"` then `selector` is `null` because all CSS selectors are present\n    * If condition was `\"or\"` then `selector` is one of the present CSS selectors of the given array\n  * In case of failure (`err` is not `null`):\n    * If condition was `\"and\"` then `selector` is one of the non-present CSS selectors of the given array\n    * If condition was `\"or\"` then `selector` is `null` because none of the CSS selectors are present\n##### Example\n```javascript\nconst selectors = \"#header \u003e h1.big-title\"\nconst pageTimeout = 5000\n\ntry {\n  await tab.waitUntilPresent(selectors, pageTimeout)\n  // The element is present in the DOM\n} catch(err) {\n  console.log(\"Oh no! Even after 5s, the element was still not present. \", err)\n}\n```\n\n# waitUntilVisible(selectors [, timeout,  condition, callback])\n**Waits for a list of `selectors` CSS selectors to be visible.**\nAborts with an error if the elements have not become visible after `timeout` milliseconds.\n\n\n\n\n\n`selectors` can be an array of CSS selectors (array of strings) or a single CSS selector (string).\n\nBy default, `condition` is `\"and\"` (wait for **all** CSS selectors) but it can be changed to `\"or\"` (wait for **any** CSS selector).\n\n### — selectors `(Array or String)`\n\nWhat to wait for. Can be an array of CSS selectors (array of strings) or a single CSS selector (string).\n\n### — timeout `(Number)`\n\nMaximum number of milliseconds to wait for, by default it is set to 5000(*optional*).\n`callback` will be called with an error if the elements have not become visible after `timeout` milliseconds.\n\n### — [condition] `(String)`\n\nWhen `selectors` is an array, this optional argument lets you choose how to wait for the CSS selectors(*optional*).\nIf `condition` is `\"and\"` (the default), the method will wait for **all** CSS selectors.\nOn the other hand, if `condition` is `\"or\"`, the method will wait for **any** CSS selector.\n\n### — callback `(Function(err, selector))`\n\nFunction called when finished(*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if the CSS selectors were not visible after `timeout` milliseconds\n* **`selector (String)`**:\n  * In case of success (`err` is `null`):\n    * If condition was `\"and\"` then `selector` is `null` because all CSS selectors are visible\n    * If condition was `\"or\"` then `selector` is one of the visible CSS selectors of the given array\n  * In case of failure (`err` is not `null`):\n    * If condition was `\"and\"` then `selector` is one of the non-visible CSS selectors of the given array\n    * If condition was `\"or\"` then `selector` is `null` because none of the CSS selectors are visible\n##### Example\n```javascript\nconst selectors = \"#header \u003e h1.big-title\"\nconst pageTimeout = 5000\n\ntry {\n  await tab.waitUntilVisible(selectors, pageTimeout)\n  // Manipulate the element here\n  // for example with a click() or evaluate()\n} catch(err) {\n  console.log(\"Oh no! Even after 5s, the element was still not visible:\", err)\n}\n```\n\n##### Example\n```javascript\nconst selectors = [\"#header \u003e h1\", \"img.product-image\"]\nconst pageTimeout = 6000\n\ntry {\n  await tab.waitUntilVisible(selectors, pageTimeout)\n  // Manipulate the element here\n  // for example with a click() or evaluate()\n} catch(err) {\n  console.log(\"Oh no! Even after 6s, at least one of the element was still not visible:\", err)\n}\n```\n\n##### Example\n```javascript\nvar selectors = [\"section.footer\", \"section.header\"]\nvar pageTimeout = 7000\n\ntry {\n  const selector = await tab.waitUntilVisible(selectors, pageTimeout)\n  console.log(\"This element is visible: \" + selector)\n  // Manipulate the element here\n  // For example with a click() or evaluate()\n} catch(err) {\n  console.log(\"Oh no! Even after 7s, all the elements were still not visible. \" + err)\n  // in this case, the callback does not return which element is not visible\n  // because ALL the elements are not visible\n}\n```\n\n# waitWhilePresent(selectors [, timeout,  condition, callback])\n**Waits for a list of `selectors` CSS selectors to become non-present in the DOM.**\nAborts with an error if the elements are still present in the DOM after `timeout` milliseconds.\n\n`selectors` can be an array of CSS selectors (array of strings) or a single CSS selector (string).\n\nBy default, `condition` is `\"and\"` (wait for **all** CSS selectors) but it can be changed to `\"or\"` (wait for **any** CSS selector).\n\n### — selectors `(Array or String)`\n\nWhat to wait for. Can be an array of CSS selectors (array of strings) or a single CSS selector (string).\n\n### — timeout `(Number)`\n\nThe maximum number of milliseconds to wait for, by default it is set to 5000 (*optional*).\n`callback` will be called with an error if the elements are still present after `timeout` milliseconds.\n\n### — [condition] `(String)`\n\nWhen `selectors` is an array, this optional argument lets you choose how to wait for the CSS selectors (*optional*).\nIf `condition` is `\"and\"` (the default), the method will wait for **all** CSS selectors.\nOn the other hand, if `condition` is `\"or\"`, the method will wait for **any** CSS selector.\n\n### — callback `(Function(err, selector))`\n\nFunction called when finished (*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if the CSS selectors were still present after `timeout` milliseconds\n* **`selector (String)`**:\n  * In case of success (`err` is `null`):\n    * If condition was `\"and\"` then `selector` is `null` because none of the CSS selectors are present\n    * If condition was `\"or\"` then `selector` is one of the non-present CSS selectors of the given array\n  * In case of failure (`err` is not `null`):\n    * If condition was `\"and\"` then `selector` is one of the still present CSS selectors of the given array\n    * If condition was `\"or\"` then `selector` is `null` because all of the CSS selectors are still present\n##### Example\n```javascript\nconst selectors = \"#header \u003e h1.big-title\"\nconst pageTimeout = 5000\n\ntry {\n  await tab.waitWhilePresent(selectors, pageTimeout)\n  // The selector has succesfully become non-present\n} catch(err) {\n  console.log(\"Oh no! Even after 5s, the element was still present:\", err)\n}\n```\n\n# waitWhileVisible(selectors [, timeout,  condition, callback])\n**Waits for a list of `selectors` CSS selectors to become non-visible.**\nAborts with an error if the elements are still visible after `timeout` milliseconds.\n\n`selectors` can be an array of CSS selectors (array of strings) or a single CSS selector (string).\n\nBy default, `condition` is `\"and\"` (wait for **all** CSS selectors) but it can be changed to `\"or\"` (wait for **any** CSS selector).\n\n### — selectors `(Array or String)`\n\nWhat to wait for. Can be an array of CSS selectors (array of strings) or a single CSS selector (string).\n\n### — timeout `(Number)`\n\nThe maximum number of milliseconds to wait for, by default it is set to 5000 (*optional*).\n`callback` will be called with an error if the elements are still visible after `timeout` milliseconds.\n\n### — [condition] `(String)`\n\nWhen `selectors` is an array, this optional argument lets you choose how to wait for the CSS selectors(*optional*).\nIf `condition` is `\"and\"` (the default), the method will wait for **all** CSS selectors.\nOn the other hand, if `condition` is `\"or\"`, the method will wait for **any** CSS selector.\n\n### — callback `(Function(err, selector))`\n\nFunction called when finished(*optional*).\n* **`err (String)`**: `null` or a description of what went wrong if the CSS selectors were still visible after `timeout` milliseconds\n* **`selector (String)`**:\n  * In case of success (`err` is `null`):\n    * If condition was `\"and\"` then `selector` is `null` because none of the CSS selectors are visible\n    * If condition was `\"or\"` then `selector` is one of the non-visible CSS selectors of the given array\n  * In case of failure (`err` is not `null`):\n    * If condition was `\"and\"` then `selector` is one of the still visible CSS selectors of the given array\n    * If condition was `\"or\"` then `selector` is `null` because all of the CSS selectors are still visible\n##### Example\n```javascript\nconst selectors = \"#header \u003e h1.big-title\"\nconst pageTimeout = 5000\n\ntry {\n  await tab.waitWhileVisible(selectors, pageTimeout)\n  // The selector has succesfully become non-visible\n} catch(err) {\n  console.log(\"Oh no! Even after 5s, the element was still visible:\", err)\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphantombuster%2Fnickjs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphantombuster%2Fnickjs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphantombuster%2Fnickjs/lists"}