diff --git a/CHANGELOG.md b/CHANGELOG.md
index a514a74..12ebf3e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # deverything
 
+## 0.33.0
+
+### Minor Changes
+
+- urls search params
+
 ## 0.32.0
 
 ### Minor Changes
diff --git a/README.md b/README.md
index a5736e5..4178763 100644
--- a/README.md
+++ b/README.md
@@ -67,7 +67,11 @@ Contributions always welcome!
 - `cleanSpaces()` trims and turns double spaces into single space
 - `clamp()` clamp number in a range
 - `first()` get the first element of an array
+- `firstKey()`
+- `firstValue()`
 - `getKeys()` get the keys of an object, includes symbols
+- `getUrlSearchParam()`
+- `getUrlSearchParams()`
 - `last()` get the last element of an array
 - ⭐ `merge()` deep merge objects
 - `moveToFirst()` move array element to first
@@ -80,6 +84,7 @@ Contributions always welcome!
 - `promiseWithTimeout()` takes a promise, a timeoutMs, and an option error as arguments. Returns a new Promise that either resolves with the value of the input promise or rejects with the provided error or a default error message if the input promise does not resolve or reject within the specified timeoutMs.
 - `scrambleText()` replace alpha chars with random chars
 - `sleep()` promise-based sleep
+- `setUrlSearchParams()`
 - `shuffle()` shuffles elements in an array
 - `toggleArrayValue()` remove/add value in array
 - `truncate()` truncate text, does not break emojis
diff --git a/package.json b/package.json
index 1278c2b..4d7503a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "deverything",
-  "version": "0.32.0",
+  "version": "0.33.0",
   "description": "Everything you need for Dev",
   "main": "./dist/index.js",
   "module": "./dist/index.mjs",
diff --git a/src/helpers/arrayIntersection.ts b/src/helpers/arrayIntersection.ts
index f680364..25c00b0 100644
--- a/src/helpers/arrayIntersection.ts
+++ b/src/helpers/arrayIntersection.ts
@@ -1,5 +1,5 @@
 import { uniqueValues } from "./uniqueValues";
 
-export const arrayIntersection = (arr1: any[], arr2: any[]) => {
+export const arrayIntersection = <T>(arr1: T[], arr2: T[]): T[] => {
   return uniqueValues(arr1.filter((value) => arr2.includes(value)));
 };
diff --git a/src/helpers/getUrlSearchParam.test.ts b/src/helpers/getUrlSearchParam.test.ts
index d4cb302..bedc864 100644
--- a/src/helpers/getUrlSearchParam.test.ts
+++ b/src/helpers/getUrlSearchParam.test.ts
@@ -27,7 +27,7 @@ describe("getUrlSearchParam", () => {
       "1"
     );
     expect(getUrlSearchParam("http://pippo.com?param=2&param=1", "param")).toBe(
-      "2"
+      "1"
     );
     expect(
       getUrlSearchParam("http://localhost/?param=1&param2=2", "param2")
diff --git a/src/helpers/getUrlSearchParam.ts b/src/helpers/getUrlSearchParam.ts
index bafef2b..3d2aa1e 100644
--- a/src/helpers/getUrlSearchParam.ts
+++ b/src/helpers/getUrlSearchParam.ts
@@ -1,12 +1,9 @@
 import { Maybe } from "../types/Maybe";
+import { getUrlSearchParams } from "./getUrlSearchParams";
 
-export const getUrlSearchParam = (urlString: Maybe<string>, param: string) => {
-  if (!urlString) return undefined;
-  try {
-    const url = new URL(urlString);
-    const value = url.searchParams.get(param);
-    return value ?? undefined;
-  } catch (_e) {
-    return undefined;
-  }
+export const getUrlSearchParam = (
+  urlString: Maybe<string>,
+  param: string
+): string | undefined => {
+  return getUrlSearchParams(urlString)[param];
 };
diff --git a/src/helpers/getUrlSearchParams.test.ts b/src/helpers/getUrlSearchParams.test.ts
new file mode 100644
index 0000000..389210b
--- /dev/null
+++ b/src/helpers/getUrlSearchParams.test.ts
@@ -0,0 +1,20 @@
+import { describe, expect, test } from "@jest/globals";
+import { getUrlSearchParams } from "./getUrlSearchParams";
+
+describe("getUrlSearchParams", () => {
+  test("found", async () => {
+    expect(getUrlSearchParams("https")).toStrictEqual({});
+    expect(getUrlSearchParams("https://www.ciao.com")).toStrictEqual({});
+    expect(getUrlSearchParams("https://www.ciao.com/")).toStrictEqual({});
+    expect(getUrlSearchParams("https://www.ciao.com/#")).toStrictEqual({});
+    expect(getUrlSearchParams("https://www.ciao.com/?param=")).toStrictEqual({
+      param: "",
+    });
+    expect(
+      getUrlSearchParams("https://www.ciao.com/?param1=123&param2=asd")
+    ).toStrictEqual({
+      param1: "123",
+      param2: "asd",
+    });
+  });
+});
diff --git a/src/helpers/getUrlSearchParams.ts b/src/helpers/getUrlSearchParams.ts
new file mode 100644
index 0000000..c5a8f10
--- /dev/null
+++ b/src/helpers/getUrlSearchParams.ts
@@ -0,0 +1,13 @@
+import { Maybe } from "../types/Maybe";
+
+export const getUrlSearchParams = (
+  urlString: Maybe<string>
+): Record<string, string> => {
+  if (!urlString) return {};
+  try {
+    const url = new URL(urlString);
+    return Object.fromEntries(url.searchParams);
+  } catch (_e) {
+    return {};
+  }
+};
diff --git a/src/helpers/index.ts b/src/helpers/index.ts
index 950032d..ebc1e2e 100644
--- a/src/helpers/index.ts
+++ b/src/helpers/index.ts
@@ -9,6 +9,8 @@ export * from "./first";
 export * from "./firstKey";
 export * from "./firstValue";
 export * from "./getKeys";
+export * from "./getUrlSearchParam";
+export * from "./getUrlSearchParams";
 export * from "./last";
 export * from "./merge";
 export * from "./moveToFirst";
@@ -21,6 +23,7 @@ export * from "./pretty";
 export * from "./promiseWithTimeout";
 export * from "./scrambleText";
 export * from "./serialize";
+export * from "./setUrlSearchParams";
 export * from "./shuffle";
 export * from "./sleep";
 export * from "./toggleArrayValue";
diff --git a/src/helpers/setUrlSearchParams.test.ts b/src/helpers/setUrlSearchParams.test.ts
new file mode 100644
index 0000000..2e18bc6
--- /dev/null
+++ b/src/helpers/setUrlSearchParams.test.ts
@@ -0,0 +1,57 @@
+import { expect, describe, test } from "@jest/globals";
+import { setUrlSearchParams } from "./setUrlSearchParams";
+
+describe("setUrlSearchParams", () => {
+  test("relative url", () => {
+    expect(setUrlSearchParams("/signin")).toBe("/signin");
+    expect(setUrlSearchParams("/signin?")).toBe("/signin");
+    expect(setUrlSearchParams("/signin?in")).toBe("/signin?in");
+    expect(setUrlSearchParams("/signin?in#sec")).toBe("/signin?in#sec");
+    expect(setUrlSearchParams("/signin?in#sec", { UPPER: "CASE" })).toBe(
+      "/signin?in=&UPPER=CASE#sec"
+    );
+    expect(
+      setUrlSearchParams("/signin?in#sec", { and: "&", equals: "=" })
+    ).toBe("/signin?in=&and=%26&equals=%3D#sec");
+  });
+
+  test("ip", () => {
+    expect(setUrlSearchParams("http://127.127.127.127", {})).toBe(
+      "http://127.127.127.127/"
+    );
+
+    expect(setUrlSearchParams("http://127.127.127.127/ok", {})).toBe(
+      "http://127.127.127.127/ok"
+    );
+    expect(
+      setUrlSearchParams("http://127.127.127.127/ok?go", { ng: true })
+    ).toBe("http://127.127.127.127/ok?go=&ng=true");
+  });
+
+  test("abs url", () => {
+    expect(setUrlSearchParams("http://localhost", {})).toBe(
+      "http://localhost/"
+    );
+    expect(setUrlSearchParams("http://dev.dev/hop", { ciao: "tu" })).toBe(
+      "http://dev.dev/hop?ciao=tu"
+    );
+    expect(
+      setUrlSearchParams("https://secure.gong/?ciao=tu", { ciao: "tu" })
+    ).toBe("https://secure.gong/?ciao=tu");
+    expect(
+      setUrlSearchParams("http://localhost/?ciao=tu", { ciao: "tu", a: "b" })
+    ).toBe("http://localhost/?ciao=tu&a=b");
+    expect(
+      setUrlSearchParams("http://localhost/?ciao=tu", { ciao: "tu", a: "" })
+    ).toBe("http://localhost/?ciao=tu&a=");
+    expect(
+      setUrlSearchParams("http://localhost/?ciao=tu", { ciao: "tu", a: 1 })
+    ).toBe("http://localhost/?ciao=tu&a=1");
+    expect(
+      setUrlSearchParams("http://localhost/?kikko=tu#", { ciao: "tu", a: 1 })
+    ).toBe("http://localhost/?kikko=tu&ciao=tu&a=1#");
+    expect(setUrlSearchParams("http://localhost/?over=1", { over: "2" })).toBe(
+      "http://localhost/?over=2"
+    );
+  });
+});
diff --git a/src/helpers/setUrlSearchParams.ts b/src/helpers/setUrlSearchParams.ts
new file mode 100644
index 0000000..e07b45f
--- /dev/null
+++ b/src/helpers/setUrlSearchParams.ts
@@ -0,0 +1,20 @@
+export const setUrlSearchParams = (
+  currentURL: string,
+  searchParams: Record<string, string | number | boolean> = {}
+) => {
+  const isRelativeUrl = currentURL.startsWith("/");
+  const url = new URL(
+    currentURL,
+    isRelativeUrl ? "https://deverything.dev/" : undefined // add base to make relative urls work
+  );
+
+  Object.entries(searchParams).forEach(([paramKey, paramValue]) => {
+    url.searchParams.set(paramKey, paramValue.toString());
+  });
+
+  if (isRelativeUrl) {
+    return url.pathname + url.search + url.hash;
+  }
+
+  return url.toString();
+};
diff --git a/src/random/randomEmail.ts b/src/random/randomEmail.ts
index a6bf9d2..79b61dd 100644
--- a/src/random/randomEmail.ts
+++ b/src/random/randomEmail.ts
@@ -2,6 +2,5 @@ import { DOMAIN_NAMES } from "../constants/domains";
 import { randomArrayItem } from "./randomArrayItem";
 import { randomHandle } from "./randomHandle";
 
-//Use randomId() to generate a unique email
 export const randomEmail = () =>
   `${randomHandle()}@${randomArrayItem(DOMAIN_NAMES)}`;
diff --git a/src/random/randomHandle.ts b/src/random/randomHandle.ts
index 817f278..979121f 100644
--- a/src/random/randomHandle.ts
+++ b/src/random/randomHandle.ts
@@ -1,10 +1,10 @@
 import { WESTERN_FIRST_NAMES, WESTERN_LAST_NAMES } from "../constants/names";
 import { randomArrayItem } from "./randomArrayItem";
-import { randomInt } from "./randomInt";
+import { randomNumericId } from "./randomNumericId";
 
 export const randomHandle = () =>
   (
     randomArrayItem(WESTERN_FIRST_NAMES) +
     "." +
     randomArrayItem(WESTERN_LAST_NAMES)
-  ).toLowerCase() + randomInt(11, 99);
+  ).toLowerCase() + randomNumericId(); // use randomNumericId too keep handles process-unique
diff --git a/src/validators/isURL.test.ts b/src/validators/isURL.test.ts
index f056c0a..36abd76 100644
--- a/src/validators/isURL.test.ts
+++ b/src/validators/isURL.test.ts
@@ -6,6 +6,9 @@ describe("isURL", function () {
     expect(isURL("a@a.a")).toBe(false);
     expect(isURL("")).toBe(false);
     expect(isURL(" ")).toBe(false);
+    expect(isURL("/localhost")).toBe(false);
+    expect(isURL("file://../localhost")).toBe(false);
+    expect(isURL("../localhost")).toBe(false);
     expect(isURL("//localhost")).toBe(false);
   });
 
diff --git a/src/validators/isURL.ts b/src/validators/isURL.ts
index 9fa3051..a623ea5 100644
--- a/src/validators/isURL.ts
+++ b/src/validators/isURL.ts
@@ -4,9 +4,9 @@ const pattern = new RegExp(
     "((\\d{1,3}\\.){3}\\d{1,3}))" + // OR ip (v4) address
     "(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // port and path
     "(\\?[;&a-z\\d%_.~+=-]*)?" + // query string
-    "(\\#[-a-z\\d_]*)?$",
+    "(\\#[-a-z\\d_]*)?$", // fragment locator
   "i"
-); // fragment locator
+);
 
 export const isURL = (arg: string) => {
   return !!arg && pattern.test(arg);